diff --git a/.agents/skills/release/SKILL.md b/.agents/skills/release/SKILL.md new file mode 100644 index 0000000..a6da93d --- /dev/null +++ b/.agents/skills/release/SKILL.md @@ -0,0 +1,154 @@ +--- +name: release +description: "Plan and publish a GitHub Release in a tag-driven repository. Use when a user asks to cut, prepare, or publish a software release, propose the next vX.Y.Z tag, draft better release notes from PRs and direct commits since the last release, update CHANGELOG.md, create the tag pinned to an exact commit, and watch the publish workflow." +--- + +# Release + +Use this skill for repos that publish from GitHub Releases and want human-written notes instead of GitHub's generated summary. + +## Guardrails + +- Prefer the repo's default branch from `gh repo view`; do not guess. +- Start from a clean working tree. If tracked files are dirty, stop and ask before continuing. +- If the current branch is not the default branch, stop and ask before switching. +- Fetch before planning: `git fetch origin --tags`. +- Fast-forward the default branch before editing: `git pull --ff-only origin `. +- Never force-push the default branch. +- Never use GitHub generated release notes for this workflow. +- Always create the release tag with a leading `v`, for example `v0.1.0`. +- Always pin the release to the exact changelog commit SHA with `gh release create --target `. +- If `origin/` moves after planning or before pushing, stop and regenerate the release plan. + +## Helper Script + +Use the bundled planner to gather release facts and raw note inputs: + +```bash +python3 .agents/skills/release/scripts/release_plan.py --output-dir .local/release +``` + +It writes: + +- `.local/release/release-plan.json` +- `.local/release/release-plan.md` + +The planner: + +- finds the latest published semver release +- counts first-parent commits on the default branch since that release +- filters leaked release-housekeeping commits such as changelog-only commits +- proposes the next tag +- groups PR-backed changes separately from direct commits on `main` +- captures contributor mentions for PR-backed items + +## Approval Prompt + +Before making any changelog edit, commit, push, tag, or release, show the user: + +- the last release tag +- the raw and meaningful commit counts since that release +- the suggested new tag and why +- whether the change looks like a minor release or a small emergency patch +- the exact commit SHA currently at `origin/` + +If the meaningful commit count is less than `3`, explicitly warn that there are not many changes in this release and ask whether they still want to proceed. + +## Notes And Changelog Rules + +- Do not copy PR titles verbatim into release notes. +- Rewrite each PR-backed item into a clearer user-facing bullet. +- For direct commits on `main` with no PR, use the commit subject and body as raw input and rewrite those too. +- Add the PR author mention on the same line for PR-backed entries. +- Keep the same substance in `CHANGELOG.md` and the GitHub release notes. +- Prefer grouped sections such as `Highlights`, `Fixes`, `Performance`, `Docs`, and `Internal` when they fit the release. +- If `CHANGELOG.md` does not exist, create it with a `# Changelog` header. +- Insert the new release section at the top, directly under the file header if there is one. +- Use a heading in this shape: + +```md +## v0.1.0 - 2026-03-15 +``` + +- If you make a dedicated changelog commit, use a subject like: + +```bash +docs: add changelog for v0.1.0 +``` + +## Versioning Heuristic + +Use the planner's suggestion unless the user overrides it. + +- Default to a minor bump: `v0.1.0` -> `v0.2.0`. +- Use a patch bump only for a small hotfix shortly after the previous release. +- Treat `v0.9.0` -> `v0.10.0` as the normal next minor bump. +- Do not jump from `v0.9.0` to `v1.0.0` unless the user explicitly asks. + +The bundled planner treats a patch release as the default only when all of these are true: + +- the last release is recent +- there are only a few meaningful commits +- the included changes are patch-sized fix/docs/ci/chore/deps style work +- there is no obvious feature or larger performance/restructure change + +## Execution Flow + +1. Prepare the repo. + +```bash +git fetch origin --tags +default_branch=$(gh repo view --json defaultBranchRef --jq '.defaultBranchRef.name') +current_branch=$(git branch --show-current) +test "$current_branch" = "$default_branch" +git pull --ff-only origin "$default_branch" +python3 .agents/skills/release/scripts/release_plan.py --output-dir .local/release +``` + +2. Read `.local/release/release-plan.md` and summarize the proposed release for approval. + +3. After approval, write: + +- `.local/release/release-notes.md` +- `CHANGELOG.md` + +4. Commit and push the changelog commit on top of the planned branch tip. + +```bash +git add CHANGELOG.md +git commit -m "docs: add changelog for " +git push origin HEAD:"$default_branch" +release_sha=$(git rev-parse HEAD) +``` + +5. Create the release from that exact commit. + +```bash +gh release create "" \ + --target "$release_sha" \ + --title "" \ + --notes-file .local/release/release-notes.md +``` + +6. Verify the release and watch the publish workflow. + +```bash +gh release view "" +run_id=$(gh run list --workflow publish.yml --event release --limit 10 --json databaseId,headSha,status,conclusion \ + --jq '.[] | select(.headSha == "'"$release_sha"'") | .databaseId' | head -n1) +gh run watch "$run_id" +``` + +If the publish workflow fails, inspect it yourself: + +```bash +gh run view "$run_id" --log-failed +``` + +## Best Practices + +- Re-read the generated notes before publishing; fix awkward wording instead of shipping raw commit text. +- Keep release bullets user-facing and outcome-oriented, not implementation-jargon heavy. +- Mention direct-to-main commits that would otherwise be invisible to GitHub's PR-based notes. +- If the approval gap was long, rerun the planner immediately before editing `CHANGELOG.md`. +- If the push to the default branch is rejected, stop and regenerate notes from the new branch tip instead of rebasing blindly. diff --git a/.agents/skills/release/agents/openai.yaml b/.agents/skills/release/agents/openai.yaml new file mode 100644 index 0000000..1520e73 --- /dev/null +++ b/.agents/skills/release/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Release" + short_description: "Plan and publish a GitHub release with a real changelog" + default_prompt: "Use $release to prepare a GitHub Release, propose the next vX.Y.Z tag, draft better notes than GitHub's defaults, update CHANGELOG.md, and publish from the exact changelog commit." diff --git a/.agents/skills/release/scripts/release_plan.py b/.agents/skills/release/scripts/release_plan.py new file mode 100644 index 0000000..cbea69e --- /dev/null +++ b/.agents/skills/release/scripts/release_plan.py @@ -0,0 +1,535 @@ +#!/usr/bin/env python3 + +from __future__ import annotations + +import argparse +import json +import os +import re +import subprocess +import sys +from collections import OrderedDict +from dataclasses import dataclass +from datetime import datetime, timezone +from pathlib import Path +from typing import Any + + +SEMVER_TAG_RE = re.compile(r"^v(\d+)\.(\d+)\.(\d+)$") +CONVENTIONAL_RE = re.compile(r"^(?P[a-z]+)(?:\([^)]+\))?(?P!)?: (?P.+)$") +TRAILING_PR_RE = re.compile(r"\s+\(#\d+\)$") +HOUSEKEEPING_SUBJECT_RES = ( + re.compile(r"^docs: (?:add|update|write) changelog\b", re.IGNORECASE), + re.compile(r"^chore(?:\(release\))?: release\b", re.IGNORECASE), + re.compile(r"^chore(?:\(release\))?: prepare v\d+\.\d+\.\d+\b", re.IGNORECASE), +) +PATCH_TYPES = {"fix", "docs", "ci", "build", "chore", "deps", "test"} +FEATURE_TYPES = {"feat", "perf", "refactor"} +TYPE_ORDER = ["feat", "fix", "perf", "refactor", "docs", "test", "ci", "build", "chore", "deps", "other"] +SECTION_LABELS = { + "feat": "Highlights", + "fix": "Fixes", + "perf": "Performance", + "refactor": "Internal", + "docs": "Docs", + "test": "Internal", + "ci": "Internal", + "build": "Internal", + "chore": "Internal", + "deps": "Internal", + "other": "Internal", +} +PATCH_WINDOW_DAYS = 7 + + +@dataclass +class ReleaseInfo: + tag: str + name: str | None + published_at: str | None + + +def run(cmd: list[str], cwd: str | None = None, check: bool = True) -> str: + proc = subprocess.run(cmd, cwd=cwd, capture_output=True, text=True) + if check and proc.returncode != 0: + raise RuntimeError( + f"Command failed ({proc.returncode}): {' '.join(cmd)}\n" + f"stdout:\n{proc.stdout}\n" + f"stderr:\n{proc.stderr}" + ) + return proc.stdout + + +def json_cmd(cmd: list[str], cwd: str | None = None) -> Any: + output = run(cmd, cwd=cwd) + if not output.strip(): + return None + return json.loads(output) + + +def git(*args: str, cwd: str) -> str: + return run(["git", *args], cwd=cwd).strip() + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description="Collect release planning facts for a GitHub-release workflow.") + parser.add_argument( + "--output-dir", + default=".local/release", + help="Directory for release-plan.json and release-plan.md (default: .local/release)", + ) + return parser.parse_args() + + +def iso_to_datetime(value: str | None) -> datetime | None: + if not value: + return None + return datetime.fromisoformat(value.replace("Z", "+00:00")) + + +def current_iso_date() -> str: + return datetime.now().date().isoformat() + + +def parse_semver(tag: str) -> tuple[int, int, int]: + match = SEMVER_TAG_RE.match(tag) + if not match: + raise ValueError(f"Unsupported tag format: {tag}") + return tuple(int(part) for part in match.groups()) + + +def format_tag(parts: tuple[int, int, int]) -> str: + major, minor, patch = parts + return f"v{major}.{minor}.{patch}" + + +def strip_conventional(subject: str) -> tuple[str, str]: + cleaned = TRAILING_PR_RE.sub("", subject).strip() + match = CONVENTIONAL_RE.match(cleaned) + if not match: + return "other", cleaned + change_type = match.group("type") + summary = match.group("summary").strip() + return change_type, summary + + +def prioritize_type(types: set[str]) -> str: + normalized = {change_type if change_type in SECTION_LABELS else "other" for change_type in types} + for change_type in TYPE_ORDER: + if change_type in normalized: + return change_type + return "other" + + +def is_housekeeping_commit(subject: str, files: list[str]) -> bool: + normalized_files = [path for path in files if path] + if normalized_files and set(normalized_files) == {"CHANGELOG.md"}: + return True + return any(pattern.search(subject) for pattern in HOUSEKEEPING_SUBJECT_RES) + + +def filter_status_lines(status_output: str, ignored_prefixes: list[str]) -> list[str]: + kept: list[str] = [] + normalized_prefixes = [prefix.rstrip("/") + "/" for prefix in ignored_prefixes if prefix and prefix != "."] + for raw_line in status_output.splitlines(): + line = raw_line.rstrip() + if not line: + continue + path_part = line[3:] if len(line) > 3 else "" + candidates = [candidate.strip() for candidate in path_part.split(" -> ")] + if candidates and all( + any(candidate == prefix[:-1] or candidate.startswith(prefix) for prefix in normalized_prefixes) + for candidate in candidates + ): + continue + kept.append(line) + return kept + + +def get_repo_info(repo_root: str) -> dict[str, Any]: + info = json_cmd(["gh", "repo", "view", "--json", "nameWithOwner,defaultBranchRef,url"], cwd=repo_root) + if not info or "nameWithOwner" not in info: + raise RuntimeError("Could not determine repository info from gh repo view.") + return info + + +def get_last_release(repo_root: str) -> ReleaseInfo | None: + releases = json_cmd( + [ + "gh", + "release", + "list", + "--exclude-drafts", + "--exclude-pre-releases", + "--limit", + "100", + "--json", + "tagName,name,publishedAt", + ], + cwd=repo_root, + ) + for release in releases or []: + tag = release.get("tagName") + if tag and SEMVER_TAG_RE.match(tag): + return ReleaseInfo(tag=tag, name=release.get("name"), published_at=release.get("publishedAt")) + return None + + +def get_commit_files(repo_root: str, sha: str) -> list[str]: + output = git("diff-tree", "--no-commit-id", "--name-only", "-r", "--root", sha, cwd=repo_root) + return [line for line in output.splitlines() if line.strip()] + + +def get_commit_metadata(repo_root: str, sha: str) -> dict[str, Any]: + output = run( + ["git", "show", "-s", "--format=%H%x1f%s%x1f%an%x1f%ae%x1f%cI%x1f%b%x1e", sha], + cwd=repo_root, + ).rstrip("\n") + if output.endswith("\x1e"): + output = output[:-1] + fields = output.split("\x1f", 5) + if len(fields) != 6: + raise RuntimeError(f"Unexpected git show output for {sha}") + _, subject, author_name, author_email, committed_at, body = fields + body = body.strip() + files = get_commit_files(repo_root, sha) + change_type, summary_seed = strip_conventional(subject) + return { + "sha": sha, + "shortSha": sha[:7], + "subject": subject, + "body": body, + "authorName": author_name, + "authorEmail": author_email, + "committedAt": committed_at, + "files": files, + "changeType": change_type, + "summarySeed": summary_seed, + } + + +def get_associated_pr(repo_root: str, repo_name: str, sha: str) -> dict[str, Any] | None: + pulls = json_cmd(["gh", "api", f"repos/{repo_name}/commits/{sha}/pulls"], cwd=repo_root) or [] + if not pulls: + return None + selected = None + for pull in pulls: + if pull.get("merge_commit_sha") == sha: + selected = pull + break + if selected is None: + selected = pulls[0] + author = selected.get("user") or {} + return { + "number": selected.get("number"), + "title": selected.get("title"), + "url": selected.get("html_url"), + "authorLogin": author.get("login"), + "mergeCommitSha": selected.get("merge_commit_sha"), + "mergedAt": selected.get("merged_at"), + } + + +def suggest_version(last_release: ReleaseInfo | None, meaningful_commits: list[dict[str, Any]]) -> dict[str, Any]: + if last_release is None: + return { + "tag": "v0.1.0", + "kind": "minor", + "reason": "No published semver release was found, so start at v0.1.0.", + "minorAlternative": None, + "patchAlternative": None, + } + + major, minor, patch = parse_semver(last_release.tag) + minor_tag = format_tag((major, minor + 1, 0)) + patch_tag = format_tag((major, minor, patch + 1)) + published_at = iso_to_datetime(last_release.published_at) + age_days = None + if published_at is not None: + age_days = (datetime.now(timezone.utc) - published_at).total_seconds() / 86400 + + types = {commit["changeType"] for commit in meaningful_commits} + patch_candidate = ( + meaningful_commits + and len(meaningful_commits) <= 3 + and (age_days is not None and age_days <= PATCH_WINDOW_DAYS) + and types.issubset(PATCH_TYPES) + and not any(change_type in FEATURE_TYPES for change_type in types) + ) + + if patch_candidate: + reason = ( + f"Patch suggested because the last release was only {age_days:.1f} days ago, " + f"there are {len(meaningful_commits)} meaningful commits, and they all look patch-sized." + ) + return { + "tag": patch_tag, + "kind": "patch", + "reason": reason, + "minorAlternative": minor_tag, + "patchAlternative": None, + } + + reason = ( + f"Minor suggested because the default release policy is a minor bump and this range includes " + f"{len(meaningful_commits)} meaningful commits" + ) + if any(change_type in FEATURE_TYPES for change_type in types): + reason += " with feature/performance/refactor work." + else: + reason += "." + return { + "tag": minor_tag, + "kind": "minor", + "reason": reason, + "minorAlternative": None, + "patchAlternative": patch_tag, + } + + +def build_release_items(repo_root: str, repo_name: str, commits: list[dict[str, Any]]) -> list[dict[str, Any]]: + grouped: "OrderedDict[str, dict[str, Any]]" = OrderedDict() + direct_count = 0 + + for commit in commits: + associated_pr = get_associated_pr(repo_root, repo_name, commit["sha"]) + if associated_pr and associated_pr.get("number") is not None: + key = f"pr-{associated_pr['number']}" + entry = grouped.get(key) + if entry is None: + entry = { + "kind": "pr", + "pr": associated_pr, + "commits": [], + "types": set(), + } + grouped[key] = entry + entry["commits"].append(commit) + entry["types"].add(commit["changeType"]) + else: + key = f"commit-{direct_count}" + direct_count += 1 + grouped[key] = { + "kind": "commit", + "commit": commit, + "types": {commit["changeType"]}, + } + + items: list[dict[str, Any]] = [] + for entry in grouped.values(): + dominant_type = prioritize_type(entry["types"]) + if entry["kind"] == "pr": + pr = entry["pr"] + commit_summaries = [commit["summarySeed"] for commit in entry["commits"]] + items.append( + { + "kind": "pr", + "section": SECTION_LABELS[dominant_type], + "changeType": dominant_type, + "pr": pr, + "summarySeed": commit_summaries[0], + "rawTitle": pr.get("title"), + "contributorMention": f"@{pr['authorLogin']}" if pr.get("authorLogin") else None, + "commits": entry["commits"], + "rawInputs": commit_summaries, + } + ) + else: + commit = entry["commit"] + items.append( + { + "kind": "commit", + "section": SECTION_LABELS[dominant_type], + "changeType": dominant_type, + "summarySeed": commit["summarySeed"], + "commit": commit, + "rawInputs": [commit["summarySeed"]], + } + ) + return items + + +def render_markdown(plan: dict[str, Any]) -> str: + lines: list[str] = [] + repo = plan["repository"]["nameWithOwner"] + lines.append("# Release Plan") + lines.append("") + lines.append(f"- Repo: `{repo}`") + lines.append(f"- Default branch: `{plan['repository']['defaultBranch']}`") + lines.append(f"- Planned branch tip: `{plan['planning']['baseSha']}`") + lines.append(f"- Current branch: `{plan['workingTree']['currentBranch']}`") + lines.append(f"- Clean working tree: `{str(plan['workingTree']['isClean']).lower()}`") + + last_release = plan.get("lastRelease") + if last_release: + lines.append(f"- Last release: `{last_release['tag']}` published `{last_release.get('publishedAt') or 'unknown'}`") + else: + lines.append("- Last release: none found") + + lines.append(f"- Commits on main since last release: `{plan['counts']['rawCommitCount']}`") + lines.append(f"- Meaningful commits after filtering housekeeping: `{plan['counts']['meaningfulCommitCount']}`") + lines.append(f"- Suggested tag: `{plan['suggestedVersion']['tag']}` ({plan['suggestedVersion']['kind']})") + lines.append(f"- Version rationale: {plan['suggestedVersion']['reason']}") + if plan["warnings"]: + lines.append("") + lines.append("## Warnings") + lines.append("") + for warning in plan["warnings"]: + lines.append(f"- {warning}") + + if plan["ignoredCommits"]: + lines.append("") + lines.append("## Ignored Housekeeping Commits") + lines.append("") + for commit in plan["ignoredCommits"]: + lines.append(f"- `{commit['shortSha']}` {commit['subject']}") + + if plan["items"]: + lines.append("") + lines.append("## Raw Release Inputs") + lines.append("") + grouped_items: "OrderedDict[str, list[dict[str, Any]]]" = OrderedDict() + for item in plan["items"]: + grouped_items.setdefault(item["section"], []).append(item) + for section, section_items in grouped_items.items(): + lines.append(f"### {section}") + lines.append("") + for item in section_items: + if item["kind"] == "pr": + pr = item["pr"] + mention = item["contributorMention"] or "unknown author" + lines.append( + f"- PR #{pr['number']}: seed `{item['summarySeed']}`; raw title `{item['rawTitle']}`; contributor {mention}" + ) + for commit in item["commits"]: + lines.append(f" - `{commit['shortSha']}` {commit['subject']}") + else: + commit = item["commit"] + lines.append(f"- Direct commit `{commit['shortSha']}`: seed `{item['summarySeed']}` from `{commit['subject']}`") + lines.append("") + + lines.append("## Changelog Heading") + lines.append("") + lines.append(f"Use this heading in `CHANGELOG.md`: `## {plan['suggestedVersion']['tag']} - {plan['generatedAtDate']}`") + lines.append("") + lines.append("Do not copy the raw titles verbatim into the release notes; rewrite them.") + return "\n".join(lines).rstrip() + "\n" + + +def main() -> int: + args = parse_args() + repo_root = git("rev-parse", "--show-toplevel", cwd=os.getcwd()) + output_dir = Path(repo_root) / args.output_dir + try: + output_dir_rel = output_dir.relative_to(Path(repo_root)).as_posix() + except ValueError: + output_dir_rel = "" + repo_info = get_repo_info(repo_root) + repo_name = repo_info["nameWithOwner"] + default_branch = repo_info["defaultBranchRef"]["name"] + remote_ref = f"refs/remotes/origin/{default_branch}" + + try: + base_sha = git("rev-parse", remote_ref, cwd=repo_root) + except RuntimeError: + base_sha = git("rev-parse", default_branch, cwd=repo_root) + + current_branch = git("branch", "--show-current", cwd=repo_root) or "(detached HEAD)" + working_tree_status = git("status", "--porcelain", "--untracked-files=all", cwd=repo_root) + filtered_status_lines = filter_status_lines(working_tree_status, [output_dir_rel]) + is_clean = len(filtered_status_lines) == 0 + local_head = git("rev-parse", "HEAD", cwd=repo_root) + + last_release = get_last_release(repo_root) + if last_release is None: + commit_range = base_sha + raw_commit_shas = git("rev-list", "--first-parent", "--reverse", base_sha, cwd=repo_root).splitlines() + else: + commit_range = f"{last_release.tag}..{base_sha}" + raw_commit_output = git("rev-list", "--first-parent", "--reverse", commit_range, cwd=repo_root) + raw_commit_shas = [line for line in raw_commit_output.splitlines() if line.strip()] + + raw_commits = [get_commit_metadata(repo_root, sha) for sha in raw_commit_shas] + + meaningful_commits: list[dict[str, Any]] = [] + ignored_commits: list[dict[str, Any]] = [] + for commit in raw_commits: + if is_housekeeping_commit(commit["subject"], commit["files"]): + ignored_commits.append(commit) + else: + meaningful_commits.append(commit) + + items = build_release_items(repo_root, repo_name, meaningful_commits) + suggested_version = suggest_version(last_release, meaningful_commits) + + warnings: list[str] = [] + if not is_clean: + warnings.append("Working tree is not clean; pause before editing or releasing.") + if current_branch != default_branch: + warnings.append(f"Current branch is {current_branch}, not the default branch {default_branch}.") + if local_head != base_sha: + warnings.append( + f"Local HEAD {local_head[:7]} does not match origin/{default_branch} {base_sha[:7]}; fast-forward before releasing." + ) + if len(meaningful_commits) < 3: + warnings.append( + f"Only {len(meaningful_commits)} meaningful commit(s) landed since the last release; warn before asking for approval." + ) + if len(meaningful_commits) == 0: + warnings.append("There are no meaningful commits to release after filtering housekeeping commits.") + + plan = { + "generatedAt": datetime.now(timezone.utc).isoformat(), + "generatedAtDate": current_iso_date(), + "repository": { + "nameWithOwner": repo_name, + "url": repo_info["url"], + "defaultBranch": default_branch, + }, + "planning": { + "baseRef": remote_ref, + "baseSha": base_sha, + "range": commit_range, + }, + "workingTree": { + "currentBranch": current_branch, + "isClean": is_clean, + "localHead": local_head, + "statusLines": filtered_status_lines, + }, + "lastRelease": ( + { + "tag": last_release.tag, + "name": last_release.name, + "publishedAt": last_release.published_at, + } + if last_release + else None + ), + "counts": { + "rawCommitCount": len(raw_commits), + "meaningfulCommitCount": len(meaningful_commits), + "releaseItemCount": len(items), + }, + "suggestedVersion": suggested_version, + "warnings": warnings, + "ignoredCommits": ignored_commits, + "rawCommits": raw_commits, + "items": items, + } + + output_dir.mkdir(parents=True, exist_ok=True) + json_path = output_dir / "release-plan.json" + markdown_path = output_dir / "release-plan.md" + json_path.write_text(json.dumps(plan, indent=2) + "\n", encoding="utf-8") + markdown_path.write_text(render_markdown(plan), encoding="utf-8") + + print(f"Wrote {json_path}") + print(f"Wrote {markdown_path}") + print(f"Suggested tag: {suggested_version['tag']}") + print(f"Meaningful commits since last release: {len(meaningful_commits)}") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/.github/actions/configure-nodejs/README.md b/.github/actions/configure-nodejs/README.md new file mode 100644 index 0000000..9389d56 --- /dev/null +++ b/.github/actions/configure-nodejs/README.md @@ -0,0 +1,45 @@ +# configure-nodejs action + +Sets up Node.js, restores a lockfile-keyed `node_modules` cache, and only runs `pnpm install --frozen-lockfile` on cache misses. + +## Required workflow usage pattern + +Use this action in a dedicated `install-deps` job first, then make all build/test jobs depend on that job with `needs: install-deps`. + +Why: + +- Cache key is based on `package.json` and `pnpm-lock.yaml`. +- When the dependency graph changes, parallel jobs can all miss cache, run full installs, and race to save the same key. +- The `install-deps` job should pass `lookup-only: "true"` so it only checks whether the cache key already exists. +- On a cache hit in `install-deps`, the action returns immediately without restoring `node_modules` and without setting up Node.js or pnpm. +- On a cache miss in `install-deps`, the action runs `pnpm install --frozen-lockfile` once and saves the cache; dependent jobs then restore `node_modules` and skip reinstalling. +- In dependent jobs, the action always sets up Node.js and pnpm first because those jobs need `pnpm` to run `typecheck`, `test`, and `pack:smoke` even when the cache restore succeeds. + +## Example + +```yaml +jobs: + install-deps: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: ./.github/actions/configure-nodejs + with: + lookup-only: "true" + + typecheck: + runs-on: ubuntu-latest + needs: install-deps + steps: + - uses: actions/checkout@v6 + - uses: ./.github/actions/configure-nodejs + - run: pnpm typecheck + + test: + runs-on: ubuntu-latest + needs: install-deps + steps: + - uses: actions/checkout@v6 + - uses: ./.github/actions/configure-nodejs + - run: pnpm test +``` diff --git a/.github/actions/configure-nodejs/action.yml b/.github/actions/configure-nodejs/action.yml new file mode 100644 index 0000000..002709c --- /dev/null +++ b/.github/actions/configure-nodejs/action.yml @@ -0,0 +1,56 @@ +name: "Configure Node.js" +description: "Install Node.js and restore cached dependencies when available" + +inputs: + node-version: + description: "Node.js version" + default: "22" + lookup-only: + description: "If true, only checks whether a cache entry exists without downloading it" + default: "false" + +runs: + using: "composite" + steps: + - name: Lookup node_modules cache + if: inputs.lookup-only == 'true' + id: cache-node-modules-lookup + uses: actions/cache@v5 + with: + path: | + node_modules + !node_modules/.cache + key: node-modules-${{ inputs.node-version }}-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('package.json', 'pnpm-lock.yaml') }} + lookup-only: true + + - uses: actions/setup-node@v5 + if: inputs.lookup-only != 'true' || steps.cache-node-modules-lookup.outputs.cache-hit != 'true' + with: + node-version: ${{ inputs.node-version }} + package-manager-cache: false + + - name: Enable Corepack + if: inputs.lookup-only != 'true' || steps.cache-node-modules-lookup.outputs.cache-hit != 'true' + shell: bash + run: corepack enable + + - name: Activate pnpm + if: inputs.lookup-only != 'true' || steps.cache-node-modules-lookup.outputs.cache-hit != 'true' + shell: bash + run: corepack prepare pnpm@10.29.3 --activate + + - name: Restore node_modules from cache + if: inputs.lookup-only != 'true' + id: cache-node-modules + uses: actions/cache@v5 + with: + path: | + node_modules + !node_modules/.cache + key: node-modules-${{ inputs.node-version }}-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('package.json', 'pnpm-lock.yaml') }} + lookup-only: false + + - name: Install dependencies + if: (inputs.lookup-only == 'true' && steps.cache-node-modules-lookup.outputs.cache-hit != 'true') || (inputs.lookup-only != 'true' && steps.cache-node-modules.outputs.cache-hit != 'true') + shell: bash + run: pnpm install --frozen-lockfile diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..a0d9c16 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,74 @@ +name: CI + +on: + push: + branches: [main] + paths-ignore: + - "**/README.md" + - "**/AGENTS.md" + - "**/CLAUDE.md" + pull_request: + branches: [main] + paths-ignore: + - "**/README.md" + - "**/AGENTS.md" + - "**/CLAUDE.md" + workflow_dispatch: + +permissions: + contents: read + +jobs: + install-deps: + name: Install Dependencies + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Install Node.js and dependencies + uses: ./.github/actions/configure-nodejs + with: + lookup-only: "true" + + typecheck: + name: Typecheck + runs-on: ubuntu-latest + needs: install-deps + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Install Node.js and dependencies + uses: ./.github/actions/configure-nodejs + + - name: Run typecheck + run: pnpm typecheck + + test: + name: Test + runs-on: ubuntu-latest + needs: install-deps + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Install Node.js and dependencies + uses: ./.github/actions/configure-nodejs + + - name: Run tests + run: pnpm test + + package-smoke: + name: Package Smoke + runs-on: ubuntu-latest + needs: install-deps + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Install Node.js and dependencies + uses: ./.github/actions/configure-nodejs + + - name: Pack and install the publishable package + run: pnpm pack:smoke diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..e3f2861 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,51 @@ +name: Publish + +on: + release: + types: [published] + workflow_dispatch: + inputs: + release_tag: + description: Release tag in the form vX.Y.Z + required: true + type: string + +permissions: + contents: read + id-token: write + +jobs: + publish: + name: Publish Package + runs-on: ubuntu-latest + env: + RELEASE_TAG: ${{ github.event_name == 'workflow_dispatch' && inputs.release_tag || github.event.release.tag_name }} + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Install Node.js and dependencies + uses: ./.github/actions/configure-nodejs + + - name: Apply release version from tag + run: pnpm release:apply-version "$RELEASE_TAG" + + - name: Run typecheck + run: pnpm typecheck + + - name: Run tests + run: pnpm test + + - name: Run package smoke install + run: pnpm pack:smoke + + - name: Configure npm authentication + if: ${{ env.NODE_AUTH_TOKEN != '' }} + run: | + cat <<'EOF' > ~/.npmrc + //registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN} + EOF + + - name: Publish openclaw-codex-app-server + run: pnpm publish --no-git-checks --access public diff --git a/.gitignore b/.gitignore index 2752eb9..79349da 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,6 @@ node_modules/ .DS_Store +.local/ +*.tgz +__pycache__/ +*.pyc diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..a1b5539 --- /dev/null +++ b/.npmignore @@ -0,0 +1,9 @@ +.github/ +.agents/ +.local/ +AGENTS.md +OVERNIGHT-TODO.md +pnpm-lock.yaml +scripts/ +src/**/*.test.ts +tsconfig.json diff --git a/LICENSE b/LICENSE index 14fac91..99a7019 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2026 +Copyright (c) 2026 PwrDrvr LLC Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index e65383c..203aa77 100644 --- a/README.md +++ b/README.md @@ -32,33 +32,33 @@ pnpm install Once the PR is merged, use `main` instead. -### 2. Point this plugin repo at your local OpenClaw checkout +### 2. Install the plugin from your local checkout -From this repository: +From the openclaw repository: ```bash -pnpm add -D openclaw@file:/absolute/path/to/openclaw -pnpm install +pnpm openclaw plugins install --link "/absolute/path/to/openclaw-codex-app-server" ``` -That makes the local `openclaw` package available to this plugin before a published release includes the new interface. +### 3. Start OpenClaw -### 3. Install the plugin from your local checkout - -From the openclaw repository: +From your OpenClaw checkout: ```bash -pnpm openclaw plugins install --link "/absolute/path/to/openclaw-codex-app-server" +pnpm gateway:watch ``` -### 4. Start OpenClaw +### Optional: override `openclaw` locally inside this repo -From your OpenClaw checkout: +This repository no longer commits a machine-local `openclaw` dev dependency, so CI stays portable. If you want a local checkout of this plugin repo to resolve `openclaw` from your own OpenClaw source tree, add a local-only override in your working copy: ```bash -pnpm gateway:watch +pnpm add -D openclaw@file:/absolute/path/to/openclaw +pnpm install ``` +That override is for local development only. Do not commit the resulting `package.json` or `pnpm-lock.yaml` changes. + ## Typical Workflow 1. Start in the Telegram or Discord conversation where you want Codex bound. diff --git a/package.json b/package.json index 1c6aff7..a53561c 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,10 @@ { - "name": "@pwrdrvr/openclaw-codex-app-server", - "version": "0.0.0-development", + "name": "openclaw-codex-app-server", + "version": "2026.3.13", "description": "OpenClaw plugin for Codex App Server conversations", + "author": "PwrDrvr LLC", "license": "MIT", + "packageManager": "pnpm@10.29.3", "type": "module", "openclaw": { "extensions": [ @@ -11,7 +13,9 @@ }, "scripts": { "test": "vitest run", - "typecheck": "tsc --noEmit" + "typecheck": "tsc --noEmit", + "pack:smoke": "node ./scripts/pack-smoke.mjs", + "release:apply-version": "node ./scripts/apply-release-version.mjs" }, "peerDependencies": { "openclaw": "*" @@ -21,7 +25,6 @@ }, "devDependencies": { "@types/node": "^24.6.0", - "openclaw": "file:/Users/huntharo/.codex/worktrees/cb00/openclaw", "typescript": "^5.9.2", "vitest": "^3.2.4" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b599255..2801b30 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: dependencies: + openclaw: + specifier: '*' + version: 2026.3.13(@napi-rs/canvas@0.1.96)(@types/express@5.0.6)(node-llama-cpp@3.16.2(typescript@5.9.3)) ws: specifier: ^8.18.3 version: 8.19.0 @@ -15,9 +18,6 @@ importers: '@types/node': specifier: ^24.6.0 version: 24.12.0 - openclaw: - specifier: file:/Users/huntharo/.codex/worktrees/cb00/openclaw - version: file:../../.codex/worktrees/cb00/openclaw(@napi-rs/canvas@0.1.96)(@types/express@5.0.6)(node-llama-cpp@3.16.2(typescript@5.9.3)) typescript: specifier: ^5.9.2 version: 5.9.3 @@ -66,74 +66,38 @@ packages: resolution: {integrity: sha512-KzLNqSg1T59sSlQvEA4EL3oDIAMidM54AB1b+UGouPFuUrrwGp2uUlZUYzIIlCvqpf7wEDh8wypqXISRItkgdg==} engines: {node: '>=20.0.0'} - '@aws-sdk/core@3.973.19': - resolution: {integrity: sha512-56KePyOcZnKTWCd89oJS1G6j3HZ9Kc+bh/8+EbvtaCCXdP6T7O7NzCiPuHRhFLWnzXIaXX3CxAz0nI5My9spHQ==} - engines: {node: '>=20.0.0'} - '@aws-sdk/core@3.973.20': resolution: {integrity: sha512-i3GuX+lowD892F3IuJf8o6AbyDupMTdyTxQrCJGcn71ni5hTZ82L4nQhcdumxZ7XPJRJJVHS/CR3uYOIIs0PVA==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-env@3.972.17': - resolution: {integrity: sha512-MBAMW6YELzE1SdkOniqr51mrjapQUv8JXSGxtwRjQV0mwVDutVsn22OPAUt4RcLRvdiHQmNBDEFP9iTeSVCOlA==} - engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-env@3.972.18': resolution: {integrity: sha512-X0B8AlQY507i5DwjLByeU2Af4ARsl9Vr84koDcXCbAkplmU+1xBFWxEPrWRAoh56waBne/yJqEloSwvRf4x6XA==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-http@3.972.19': - resolution: {integrity: sha512-9EJROO8LXll5a7eUFqu48k6BChrtokbmgeMWmsH7lBb6lVbtjslUYz/ShLi+SHkYzTomiGBhmzTW7y+H4BxsnA==} - engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-http@3.972.20': resolution: {integrity: sha512-ey9Lelj001+oOfrbKmS6R2CJAiXX7QKY4Vj9VJv6L2eE6/VjD8DocHIoYqztTm70xDLR4E1jYPTKfIui+eRNDA==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-ini@3.972.18': - resolution: {integrity: sha512-vthIAXJISZnj2576HeyLBj4WTeX+I7PwWeRkbOa0mVX39K13SCGxCgOFuKj2ytm9qTlLOmXe4cdEnroteFtJfw==} - engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-ini@3.972.20': resolution: {integrity: sha512-5flXSnKHMloObNF+9N0cupKegnH1Z37cdVlpETVgx8/rAhCe+VNlkcZH3HDg2SDn9bI765S+rhNPXGDJJPfbtA==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-login@3.972.18': - resolution: {integrity: sha512-kINzc5BBxdYBkPZ0/i1AMPMOk5b5QaFNbYMElVw5QTX13AKj6jcxnv/YNl9oW9mg+Y08ti19hh01HhyEAxsSJQ==} - engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-login@3.972.20': resolution: {integrity: sha512-gEWo54nfqp2jABMu6HNsjVC4hDLpg9HC8IKSJnp0kqWtxIJYHTmiLSsIfI4ScQjxEwpB+jOOH8dOLax1+hy/Hw==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-node@3.972.19': - resolution: {integrity: sha512-yDWQ9dFTr+IMxwanFe7+tbN5++q8psZBjlUwOiCXn1EzANoBgtqBwcpYcHaMGtn0Wlfj4NuXdf2JaEx1lz5RaQ==} - engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-node@3.972.21': resolution: {integrity: sha512-hah8if3/B/Q+LBYN5FukyQ1Mym6PLPDsBOBsIgNEYD6wLyZg0UmUF/OKIVC3nX9XH8TfTPuITK+7N/jenVACWA==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-process@3.972.17': - resolution: {integrity: sha512-c8G8wT1axpJDgaP3xzcy+q8Y1fTi9A2eIQJvyhQ9xuXrUZhlCfXbC0vM9bM1CUXiZppFQ1p7g0tuUMvil/gCPg==} - engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-process@3.972.18': resolution: {integrity: sha512-Tpl7SRaPoOLT32jbTWchPsn52hYYgJ0kpiFgnwk8pxTANQdUymVSZkzFvv1+oOgZm1CrbQUP9MBeoMZ9IzLZjA==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-sso@3.972.18': - resolution: {integrity: sha512-YHYEfj5S2aqInRt5ub8nDOX8vAxgMvd84wm2Y3WVNfFa/53vOv9T7WOAqXI25qjj3uEcV46xxfqdDQk04h5XQA==} - engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-sso@3.972.20': resolution: {integrity: sha512-p+R+PYR5Z7Gjqf/6pvbCnzEHcqPCpLzR7Yf127HjJ6EAb4hUcD+qsNRnuww1sB/RmSeCLxyay8FMyqREw4p1RA==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-web-identity@3.972.18': - resolution: {integrity: sha512-OqlEQpJ+J3T5B96qtC1zLLwkBloechP+fezKbCH0sbd2cCc0Ra55XpxWpk/hRj69xAOYtHvoC4orx6eTa4zU7g==} - engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-web-identity@3.972.20': resolution: {integrity: sha512-rWCmh8o7QY4CsUj63qopzMzkDq/yPpkrpb+CnjBEFSOg/02T/we7sSTVg4QsDiVS9uwZ8VyONhq98qt+pIh3KA==} engines: {node: '>=20.0.0'} @@ -146,34 +110,18 @@ packages: resolution: {integrity: sha512-VWndapHYCfwLgPpCb/xwlMKG4imhFzKJzZcKOEioGn7OHY+6gdr0K7oqy1HZgbLa3ACznZ9fku+DzmAi8fUC0g==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-host-header@3.972.7': - resolution: {integrity: sha512-aHQZgztBFEpDU1BB00VWCIIm85JjGjQW1OG9+98BdmaOpguJvzmXBGbnAiYcciCd+IS4e9BEq664lhzGnWJHgQ==} - engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-host-header@3.972.8': resolution: {integrity: sha512-wAr2REfKsqoKQ+OkNqvOShnBoh+nkPurDKW7uAeVSu6kUECnWlSJiPvnoqxGlfousEY/v9LfS9sNc46hjSYDIQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-logger@3.972.7': - resolution: {integrity: sha512-LXhiWlWb26txCU1vcI9PneESSeRp/RYY/McuM4SpdrimQR5NgwaPb4VJCadVeuGWgh6QmqZ6rAKSoL1ob16W6w==} - engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-logger@3.972.8': resolution: {integrity: sha512-CWl5UCM57WUFaFi5kB7IBY1UmOeLvNZAZ2/OZ5l20ldiJ3TiIz1pC65gYj8X0BCPWkeR1E32mpsCk1L1I4n+lA==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-recursion-detection@3.972.7': - resolution: {integrity: sha512-l2VQdcBcYLzIzykCHtXlbpiVCZ94/xniLIkAj0jpnpjY4xlgZx7f56Ypn+uV1y3gG0tNVytJqo3K9bfMFee7SQ==} - engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-recursion-detection@3.972.8': resolution: {integrity: sha512-BnnvYs2ZEpdlmZ2PNlV2ZyQ8j8AEkMTjN79y/YA475ER1ByFYrkVR85qmhni8oeTaJcDqbx364wDpitDAA/wCA==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-user-agent@3.972.20': - resolution: {integrity: sha512-3kNTLtpUdeahxtnJRnj/oIdLAUdzTfr9N40KtxNhtdrq+Q1RPMdCJINRXq37m4t5+r3H70wgC3opW46OzFcZYA==} - engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-user-agent@3.972.21': resolution: {integrity: sha512-62XRl1GDYPpkt7cx1AX1SPy9wgNE9Iw/NPuurJu4lmhCWS7sGKO+kS53TQ8eRmIxy3skmvNInnk0ZbWrU5Dpyg==} engines: {node: '>=20.0.0'} @@ -186,22 +134,10 @@ packages: resolution: {integrity: sha512-SlDol5Z+C7Ivnc2rKGqiqfSUmUZzY1qHfVs9myt/nxVwswgfpjdKahyTzLTx802Zfq0NFRs7AejwKzzzl5Co2w==} engines: {node: '>=20.0.0'} - '@aws-sdk/nested-clients@3.996.8': - resolution: {integrity: sha512-6HlLm8ciMW8VzfB80kfIx16PBA9lOa9Dl+dmCBi78JDhvGlx3I7Rorwi5PpVRkL31RprXnYna3yBf6UKkD/PqA==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/region-config-resolver@3.972.7': - resolution: {integrity: sha512-/Ev/6AI8bvt4HAAptzSjThGUMjcWaX3GX8oERkB0F0F9x2dLSBdgFDiyrRz3i0u0ZFZFQ1b28is4QhyqXTUsVA==} - engines: {node: '>=20.0.0'} - '@aws-sdk/region-config-resolver@3.972.8': resolution: {integrity: sha512-1eD4uhTDeambO/PNIDVG19A6+v4NdD7xzwLHDutHsUqz0B+i661MwQB2eYO4/crcCvCiQG4SRm1k81k54FEIvw==} engines: {node: '>=20.0.0'} - '@aws-sdk/token-providers@3.1005.0': - resolution: {integrity: sha512-vMxd+ivKqSxU9bHx5vmAlFKDAkjGotFU56IOkDa5DaTu1WWwbcse0yFHEm9I537oVvodaiwMl3VBwgHfzQ2rvw==} - engines: {node: '>=20.0.0'} - '@aws-sdk/token-providers@3.1007.0': resolution: {integrity: sha512-kKvVyr53vvVc5k6RbvI6jhafxufxO2SkEw8QeEzJqwOXH/IMY7Cm0IyhnBGdqj80iiIIiIM2jGe7Fn3TIdwdrw==} engines: {node: '>=20.0.0'} @@ -210,18 +146,10 @@ packages: resolution: {integrity: sha512-KCPLuTqN9u0Rr38Arln78fRG9KXpzsPWmof+PZzfAHMMQq2QED6YjQrkrfiH7PDefLWEposY1o4/eGwrmKA4JA==} engines: {node: '>=20.0.0'} - '@aws-sdk/types@3.973.5': - resolution: {integrity: sha512-hl7BGwDCWsjH8NkZfx+HgS7H2LyM2lTMAI7ba9c8O0KqdBLTdNJivsHpqjg9rNlAlPyREb6DeDRXUl0s8uFdmQ==} - engines: {node: '>=20.0.0'} - '@aws-sdk/types@3.973.6': resolution: {integrity: sha512-Atfcy4E++beKtwJHiDln2Nby8W/mam64opFPTiHEqgsthqeydFS1pY+OUlN1ouNOmf8ArPU/6cDS65anOP3KQw==} engines: {node: '>=20.0.0'} - '@aws-sdk/util-endpoints@3.996.4': - resolution: {integrity: sha512-Hek90FBmd4joCFj+Vc98KLJh73Zqj3s2W56gjAcTkrNLMDI5nIFkG9YpfcJiVI1YlE2Ne1uOQNe+IgQ/Vz2XRA==} - engines: {node: '>=20.0.0'} - '@aws-sdk/util-endpoints@3.996.5': resolution: {integrity: sha512-Uh93L5sXFNbyR5sEPMzUU8tJ++Ku97EY4udmC01nB8Zu+xfBPwpIwJ6F7snqQeq8h2pf+8SGN5/NoytfKgYPIw==} engines: {node: '>=20.0.0'} @@ -234,21 +162,9 @@ packages: resolution: {integrity: sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/util-user-agent-browser@3.972.7': - resolution: {integrity: sha512-7SJVuvhKhMF/BkNS1n0QAJYgvEwYbK2QLKBrzDiwQGiTRU6Yf1f3nehTzm/l21xdAOtWSfp2uWSddPnP2ZtsVw==} - '@aws-sdk/util-user-agent-browser@3.972.8': resolution: {integrity: sha512-B3KGXJviV2u6Cdw2SDY2aDhoJkVfY/Q/Trwk2CMSkikE1Oi6gRzxhvhIfiRpHfmIsAhV4EA54TVEX8K6CbHbkA==} - '@aws-sdk/util-user-agent-node@3.973.5': - resolution: {integrity: sha512-Dyy38O4GeMk7UQ48RupfHif//gqnOPbq/zlvRssc11E2mClT+aUfc3VS2yD8oLtzqO3RsqQ9I3gOBB4/+HjPOw==} - engines: {node: '>=20.0.0'} - peerDependencies: - aws-crt: '>=1.0.0' - peerDependenciesMeta: - aws-crt: - optional: true - '@aws-sdk/util-user-agent-node@3.973.7': resolution: {integrity: sha512-Hz6EZMUAEzqUd7e+vZ9LE7mn+5gMbxltXy18v+YSFY+9LBJz15wkNZvw5JqfX3z0FS9n3bgUtz3L5rAsfh4YlA==} engines: {node: '>=20.0.0'} @@ -258,10 +174,6 @@ packages: aws-crt: optional: true - '@aws-sdk/xml-builder@3.972.10': - resolution: {integrity: sha512-OnejAIVD+CxzyAUrVic7lG+3QRltyja9LoNqCE/1YVs8ichoTbJlVSaZ9iSMcnHLyzrSNtvaOGjSDRP+d/ouFA==} - engines: {node: '>=20.0.0'} - '@aws-sdk/xml-builder@3.972.11': resolution: {integrity: sha512-iitV/gZKQMvY9d7ovmyFnFuTHbBAtrmLnvaSb/3X8vOKyevwtpmEtyc8AdhVWZe0pI/1GsHxlEvQeOePFzy7KQ==} engines: {node: '>=20.0.0'} @@ -807,22 +719,22 @@ packages: resolution: {integrity: sha512-faGUlTcXka5l7rv0lP3K3vGW/ejRuOS24RR2aSFWREUQqzjgdsuWNo/IiPqL3kWRGt6Ahl2+qcDAwtdeWeuGUw==} hasBin: true - '@mariozechner/pi-agent-core@0.57.1': - resolution: {integrity: sha512-WXsBbkNWOObFGHkhixaT8GXJpHDd3+fn8QntYF+4R8Sa9WB90ENXWidO6b7vcKX+JX0jjO5dIsQxmzosARJKlg==} + '@mariozechner/pi-agent-core@0.58.0': + resolution: {integrity: sha512-zhkwx3Wdo27snVfnJWi7l+wyU4XlazkeunTtz4e500GC+ufGOp4C3aIf0XiO5ZOtTE/0lvUiG2bWULR/i4lgUQ==} engines: {node: '>=20.0.0'} - '@mariozechner/pi-ai@0.57.1': - resolution: {integrity: sha512-Bd/J4a3YpdzJVyHLih0vDSdB0QPL4ti0XsAwtHOK/8eVhB0fHM1CpcgIrcBFJ23TMcKXMi0qamz18ERfp8tmgg==} + '@mariozechner/pi-ai@0.58.0': + resolution: {integrity: sha512-3TrkJ9QcBYFPo4NxYluhd+JQ4M+98RaEkNPMrLFU4wK4GMFVtsL3kp1YJ/oj7X0eqKuuDKbHj6MdoMZeT2TCvA==} engines: {node: '>=20.0.0'} hasBin: true - '@mariozechner/pi-coding-agent@0.57.1': - resolution: {integrity: sha512-u5MQEduj68rwVIsRsqrWkJYiJCyPph/a6bMoJAQKo1sb+Pc17Y/ojwa+wGssnUMjEB38AQKofWTVe8NFEpSWNw==} + '@mariozechner/pi-coding-agent@0.58.0': + resolution: {integrity: sha512-aCoqIMfcFWwuZrLC4MC1EnHwUrqo+ppamXlNYk5+nANH8U+51AP8OUqOUqT9NSHO9ZdItheU9wCqt7wPf5Ah8A==} engines: {node: '>=20.6.0'} hasBin: true - '@mariozechner/pi-tui@0.57.1': - resolution: {integrity: sha512-cjoRghLbeAHV0tTJeHgZXaryUi5zzBZofeZ7uJun1gztnckLLRjoVeaPTujNlc5BIfyKvFqhh1QWCZng/MXlpg==} + '@mariozechner/pi-tui@0.58.0': + resolution: {integrity: sha512-luRbQlk0ZCbYGCtCrKTqQX0ECKNYPj7OSlxKMXEY0B3bA6s4f/Xj0aLPiKlhsIynC2dPQmijA44ZDfrWFniWwA==} engines: {node: '>=20.0.0'} '@mistralai/mistralai@1.14.1': @@ -1354,10 +1266,6 @@ packages: peerDependencies: '@types/express': ^5.0.0 - '@slack/logger@4.0.0': - resolution: {integrity: sha512-Wz7QYfPAlG/DR+DfABddUZeNgoeY7d1J39OCR2jR+v7VBsB8ezulDK5szTnDDPDwLH5IWhLvXIHlCFZV7MSKgA==} - engines: {node: '>= 18', npm: '>= 8.6.0'} - '@slack/logger@4.0.1': resolution: {integrity: sha512-6cmdPrV/RYfd2U0mDGiMK8S7OJqpCTm7enMLRR3edccsPX8j7zXTLnaEF4fhxxJJTAIOil6+qZrnUPTuaLvwrQ==} engines: {node: '>= 18', npm: '>= 8.6.0'} @@ -1370,10 +1278,6 @@ packages: resolution: {integrity: sha512-VaapvmrAifeFLAFaDPfGhEwwunTKsI6bQhYzxRXw7BSujZUae5sANO76WqlVsLXuhVtCVrBWPiS2snAQR2RHJQ==} engines: {node: '>= 18', npm: '>= 8.6.0'} - '@slack/types@2.20.0': - resolution: {integrity: sha512-PVF6P6nxzDMrzPC8fSCsnwaI+kF8YfEpxf3MqXmdyjyWTYsZQURpkK7WWUWvP5QpH55pB7zyYL9Qem/xSgc5VA==} - engines: {node: '>= 12.13.0', npm: '>= 6.12.0'} - '@slack/types@2.20.1': resolution: {integrity: sha512-eWX2mdt1ktpn8+40iiMc404uGrih+2fxiky3zBcPjtXKj6HLRdYlmhrPkJi7JTJm8dpXR6BWVWEDBXtaWMKD6A==} engines: {node: '>= 12.13.0', npm: '>= 6.12.0'} @@ -1390,10 +1294,6 @@ packages: resolution: {integrity: sha512-YxFiiG4YDAtX7WMN7RuhHZLeTmRRAOyCbr+zB8e3AQzHPnUhS8zXjB1+cniPVQI3xbWsQPM0X2aaIkO/ME0ymw==} engines: {node: '>=18.0.0'} - '@smithy/core@3.23.10': - resolution: {integrity: sha512-pn0HaJpxmdeCLdbAm79SUjX8IPiej9ANHNHec4K4u5Bkf5BqYCbAgK3c8NTCVf44DnlWJK7W1mimlgBPUQ3IlA==} - engines: {node: '>=18.0.0'} - '@smithy/core@3.23.11': resolution: {integrity: sha512-952rGf7hBRnhUIaeLp6q4MptKW8sPFe5VvkoZ5qIzFAtx6c/QZ/54FS3yootsyUSf9gJX/NBqEBNdNR7jMIlpQ==} engines: {node: '>=18.0.0'} @@ -1422,10 +1322,6 @@ packages: resolution: {integrity: sha512-+yNuTiyBACxOJUTvbsNsSOfH9G9oKbaJE1lNL3YHpGcuucl6rPZMi3nrpehpVOVR2E07YqFFmtwpImtpzlouHQ==} engines: {node: '>=18.0.0'} - '@smithy/fetch-http-handler@5.3.14': - resolution: {integrity: sha512-Aswg1yMsujkikRVv+JIDw2ybTgx0cnTnv7pMee46OX6lTMwk/QpH1lbx3vN3feMwyNrFcSUbYBtbgwHXXn3CIA==} - engines: {node: '>=18.0.0'} - '@smithy/fetch-http-handler@5.3.15': resolution: {integrity: sha512-T4jFU5N/yiIfrtrsb9uOQn7RdELdM/7HbyLNr6uO/mpkj1ctiVs7CihVr51w4LyQlXWDpXFn4BElf1WmQvZu/A==} engines: {node: '>=18.0.0'} @@ -1450,26 +1346,14 @@ packages: resolution: {integrity: sha512-YE58Yz+cvFInWI/wOTrB+DbvUVz/pLn5mC5MvOV4fdRUc6qGwygyngcucRQjAhiCEbmfLOXX0gntSIcgMvAjmA==} engines: {node: '>=18.0.0'} - '@smithy/middleware-endpoint@4.4.24': - resolution: {integrity: sha512-k7SZG+7IbS4fVAI47p+QixmcjqliCoZ7T5ZtAJMHyViiv7AhMC9aXtgxvNQ8TQmbUe7kotsvW2XeEEqnTmdOXg==} - engines: {node: '>=18.0.0'} - '@smithy/middleware-endpoint@4.4.25': resolution: {integrity: sha512-dqjLwZs2eBxIUG6Qtw8/YZ4DvzHGIf0DA18wrgtfP6a50UIO7e2nY0FPdcbv5tVJKqWCCU5BmGMOUwT7Puan+A==} engines: {node: '>=18.0.0'} - '@smithy/middleware-retry@4.4.41': - resolution: {integrity: sha512-qjeS0KGftfz2CL4/IziPmQurzemKRPh6sekt3IFbj1519nkj+JM+RcdjVrC1AQFFZhmW3zz7KqwOgN+qJZeVlQ==} - engines: {node: '>=18.0.0'} - '@smithy/middleware-retry@4.4.42': resolution: {integrity: sha512-vbwyqHRIpIZutNXZpLAozakzamcINaRCpEy1MYmK6xBeW3xN+TyPRA123GjXnuxZIjc9848MRRCugVMTXxC4Eg==} engines: {node: '>=18.0.0'} - '@smithy/middleware-serde@4.2.13': - resolution: {integrity: sha512-appEschlOmriCVGLYTTjKdbnXIZ55XT9TsV+aGuj5Jiw988gmEZwJwPkYqlZdwajMKgfxt5epjFTGriyYf4Kiw==} - engines: {node: '>=18.0.0'} - '@smithy/middleware-serde@4.2.14': resolution: {integrity: sha512-+CcaLoLa5apzSRtloOyG7lQvkUw2ZDml3hRh4QiG9WyEPfW5Ke/3tPOPiPjUneuT59Tpn8+c3RVaUvvkkwqZwg==} engines: {node: '>=18.0.0'} @@ -1482,10 +1366,6 @@ packages: resolution: {integrity: sha512-tr2oKX2xMcO+rBOjobSwVAkV05SIfUKz8iI53rzxEmgW3GOOPOv0UioSDk+J8OpRQnpnhsO3Af6IEBabQBVmiw==} engines: {node: '>=18.0.0'} - '@smithy/node-http-handler@4.4.15': - resolution: {integrity: sha512-2z3Z7Qfts2Eui5Oy+MLJjwKx1LT0Hm/b6W0XJXkUIFHP1W9D4BhdvxWW2W5xPP92CoXO+B4C/zSH67uIxMkWoA==} - engines: {node: '>=18.0.0'} - '@smithy/node-http-handler@4.4.16': resolution: {integrity: sha512-ULC8UCS/HivdCB3jhi+kLFYe4B5gxH2gi9vHBfEIiRrT2jfKiZNiETJSlzRtE6B26XbBHjPtc8iZKSNqMol9bw==} engines: {node: '>=18.0.0'} @@ -1518,10 +1398,6 @@ packages: resolution: {integrity: sha512-B/FBwO3MVOL00DaRSXfXfa/TRXRheagt/q5A2NM13u7q+sHS59EOVGQNfG7DkmVtdQm5m3vOosoKAXSqn/OEgw==} engines: {node: '>=18.0.0'} - '@smithy/smithy-client@4.12.4': - resolution: {integrity: sha512-kbFGh3QrUj7Z9zYHCip+dGVyRGiFo6JK0A+9InOwmU4ZCkJs3HKhjLL/ABe5I8kp9uScqrftcWrDh7YxlWmmZA==} - engines: {node: '>=18.0.0'} - '@smithy/smithy-client@4.12.5': resolution: {integrity: sha512-UqwYawyqSr/aog8mnLnfbPurS0gi4G7IYDcD28cUIBhsvWs1+rQcL2IwkUQ+QZ7dibaoRzhNF99fAQ9AUcO00w==} engines: {node: '>=18.0.0'} @@ -1558,18 +1434,10 @@ packages: resolution: {integrity: sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ==} engines: {node: '>=18.0.0'} - '@smithy/util-defaults-mode-browser@4.3.40': - resolution: {integrity: sha512-TB++dVe/aHkhCw8+fVUiGEEyz70Drftze6uk5VGBDJAjEj2mqNFftkeY7Jyit3uui346NkZxzLMGM0yzD/S8og==} - engines: {node: '>=18.0.0'} - '@smithy/util-defaults-mode-browser@4.3.41': resolution: {integrity: sha512-M1w1Ux0rSVvBOxIIiqbxvZvhnjQ+VUjJrugtORE90BbadSTH+jsQL279KRL3Hv0w69rE7EuYkV/4Lepz/NBW9g==} engines: {node: '>=18.0.0'} - '@smithy/util-defaults-mode-node@4.2.43': - resolution: {integrity: sha512-cHmr8Q1BJstJC8ahvYrcyqjSIwrgLbpphOYmfMvF+EVsKUU52b3DDLb0SyiAzR16o7FR1r2IVUFfWWu7ADh1iw==} - engines: {node: '>=18.0.0'} - '@smithy/util-defaults-mode-node@4.2.44': resolution: {integrity: sha512-YPze3/lD1KmWuZsl9JlfhcgGLX7AXhSoaCDtiPntUjNW5/YY0lOHjkcgxyE9x/h5vvS1fzDifMGjzqnNlNiqOQ==} engines: {node: '>=18.0.0'} @@ -1590,10 +1458,6 @@ packages: resolution: {integrity: sha512-1zopLDUEOwumjcHdJ1mwBHddubYF8GMQvstVCLC54Y46rqoHwlIU+8ZzUeaBcD+WCJHyDGSeZ2ml9YSe9aqcoQ==} engines: {node: '>=18.0.0'} - '@smithy/util-stream@4.5.18': - resolution: {integrity: sha512-o0hxsNp2rC7Kz93RNER/mv5G60kntYPPjV9e9Zoa3Mm455bCGHlFW6TywziCQRlLzvrQj/mmWJimAvJWF/wfjg==} - engines: {node: '>=18.0.0'} - '@smithy/util-stream@4.5.19': resolution: {integrity: sha512-v4sa+3xTweL1CLO2UP0p7tvIMH/Rq1X4KKOxd568mpe6LSLMQCnDHs4uv7m3ukpl3HvcN2JH6jiCS0SNRXKP/w==} engines: {node: '>=18.0.0'} @@ -2807,6 +2671,7 @@ packages: music-metadata@11.12.2: resolution: {integrity: sha512-LsJHPigbRdAD49lcLAPtg5bjsIkc79mEbi9yV2NsWb9cA5jYG+4qLG2Lt3cTXHhCSEh5rFbed5AY8RIT0SXJ3A==} engines: {node: '>=18'} + deprecated: Missing TypeScript declarations mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} @@ -2913,8 +2778,8 @@ packages: zod: optional: true - openclaw@file:../../.codex/worktrees/cb00/openclaw: - resolution: {directory: ../../.codex/worktrees/cb00/openclaw, type: directory} + openclaw@2026.3.13: + resolution: {integrity: sha512-/juSUb070Xz8K8CnShjaZQr7CVtRaW4FbR93lgr1hLepcRSbyz2PQR+V4w5giVWkea61opXWPA6Vb8dybaztFg==} engines: {node: '>=22.16.0'} hasBin: true peerDependencies: @@ -3712,7 +3577,7 @@ snapshots: '@aws-crypto/crc32@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.973.5 + '@aws-sdk/types': 3.973.6 tslib: 2.8.1 '@aws-crypto/sha256-browser@5.2.0': @@ -3737,7 +3602,7 @@ snapshots: '@aws-crypto/util@5.2.0': dependencies: - '@aws-sdk/types': 3.973.5 + '@aws-sdk/types': 3.973.6 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 @@ -3745,49 +3610,49 @@ snapshots: dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.973.19 - '@aws-sdk/credential-provider-node': 3.972.19 + '@aws-sdk/core': 3.973.20 + '@aws-sdk/credential-provider-node': 3.972.21 '@aws-sdk/eventstream-handler-node': 3.972.10 '@aws-sdk/middleware-eventstream': 3.972.7 - '@aws-sdk/middleware-host-header': 3.972.7 - '@aws-sdk/middleware-logger': 3.972.7 - '@aws-sdk/middleware-recursion-detection': 3.972.7 - '@aws-sdk/middleware-user-agent': 3.972.20 + '@aws-sdk/middleware-host-header': 3.972.8 + '@aws-sdk/middleware-logger': 3.972.8 + '@aws-sdk/middleware-recursion-detection': 3.972.8 + '@aws-sdk/middleware-user-agent': 3.972.21 '@aws-sdk/middleware-websocket': 3.972.12 - '@aws-sdk/region-config-resolver': 3.972.7 + '@aws-sdk/region-config-resolver': 3.972.8 '@aws-sdk/token-providers': 3.1007.0 - '@aws-sdk/types': 3.973.5 - '@aws-sdk/util-endpoints': 3.996.4 - '@aws-sdk/util-user-agent-browser': 3.972.7 - '@aws-sdk/util-user-agent-node': 3.973.5 + '@aws-sdk/types': 3.973.6 + '@aws-sdk/util-endpoints': 3.996.5 + '@aws-sdk/util-user-agent-browser': 3.972.8 + '@aws-sdk/util-user-agent-node': 3.973.7 '@smithy/config-resolver': 4.4.11 - '@smithy/core': 3.23.10 + '@smithy/core': 3.23.11 '@smithy/eventstream-serde-browser': 4.2.12 '@smithy/eventstream-serde-config-resolver': 4.3.12 '@smithy/eventstream-serde-node': 4.2.12 - '@smithy/fetch-http-handler': 5.3.14 + '@smithy/fetch-http-handler': 5.3.15 '@smithy/hash-node': 4.2.12 '@smithy/invalid-dependency': 4.2.12 '@smithy/middleware-content-length': 4.2.12 - '@smithy/middleware-endpoint': 4.4.24 - '@smithy/middleware-retry': 4.4.41 - '@smithy/middleware-serde': 4.2.13 + '@smithy/middleware-endpoint': 4.4.25 + '@smithy/middleware-retry': 4.4.42 + '@smithy/middleware-serde': 4.2.14 '@smithy/middleware-stack': 4.2.12 '@smithy/node-config-provider': 4.3.12 - '@smithy/node-http-handler': 4.4.15 + '@smithy/node-http-handler': 4.4.16 '@smithy/protocol-http': 5.3.12 - '@smithy/smithy-client': 4.12.4 + '@smithy/smithy-client': 4.12.5 '@smithy/types': 4.13.1 '@smithy/url-parser': 4.2.12 '@smithy/util-base64': 4.3.2 '@smithy/util-body-length-browser': 4.2.2 '@smithy/util-body-length-node': 4.2.3 - '@smithy/util-defaults-mode-browser': 4.3.40 - '@smithy/util-defaults-mode-node': 4.2.43 + '@smithy/util-defaults-mode-browser': 4.3.41 + '@smithy/util-defaults-mode-node': 4.2.44 '@smithy/util-endpoints': 3.3.3 '@smithy/util-middleware': 4.2.12 '@smithy/util-retry': 4.2.12 - '@smithy/util-stream': 4.5.18 + '@smithy/util-stream': 4.5.19 '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 transitivePeerDependencies: @@ -3838,22 +3703,6 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/core@3.973.19': - dependencies: - '@aws-sdk/types': 3.973.5 - '@aws-sdk/xml-builder': 3.972.10 - '@smithy/core': 3.23.10 - '@smithy/node-config-provider': 4.3.12 - '@smithy/property-provider': 4.2.12 - '@smithy/protocol-http': 5.3.12 - '@smithy/signature-v4': 5.3.12 - '@smithy/smithy-client': 4.12.4 - '@smithy/types': 4.13.1 - '@smithy/util-base64': 4.3.2 - '@smithy/util-middleware': 4.2.12 - '@smithy/util-utf8': 4.2.2 - tslib: 2.8.1 - '@aws-sdk/core@3.973.20': dependencies: '@aws-sdk/types': 3.973.6 @@ -3870,14 +3719,6 @@ snapshots: '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - '@aws-sdk/credential-provider-env@3.972.17': - dependencies: - '@aws-sdk/core': 3.973.19 - '@aws-sdk/types': 3.973.5 - '@smithy/property-provider': 4.2.12 - '@smithy/types': 4.13.1 - tslib: 2.8.1 - '@aws-sdk/credential-provider-env@3.972.18': dependencies: '@aws-sdk/core': 3.973.20 @@ -3886,19 +3727,6 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/credential-provider-http@3.972.19': - dependencies: - '@aws-sdk/core': 3.973.19 - '@aws-sdk/types': 3.973.5 - '@smithy/fetch-http-handler': 5.3.14 - '@smithy/node-http-handler': 4.4.15 - '@smithy/property-provider': 4.2.12 - '@smithy/protocol-http': 5.3.12 - '@smithy/smithy-client': 4.12.4 - '@smithy/types': 4.13.1 - '@smithy/util-stream': 4.5.18 - tslib: 2.8.1 - '@aws-sdk/credential-provider-http@3.972.20': dependencies: '@aws-sdk/core': 3.973.20 @@ -3912,25 +3740,6 @@ snapshots: '@smithy/util-stream': 4.5.19 tslib: 2.8.1 - '@aws-sdk/credential-provider-ini@3.972.18': - dependencies: - '@aws-sdk/core': 3.973.19 - '@aws-sdk/credential-provider-env': 3.972.17 - '@aws-sdk/credential-provider-http': 3.972.19 - '@aws-sdk/credential-provider-login': 3.972.18 - '@aws-sdk/credential-provider-process': 3.972.17 - '@aws-sdk/credential-provider-sso': 3.972.18 - '@aws-sdk/credential-provider-web-identity': 3.972.18 - '@aws-sdk/nested-clients': 3.996.8 - '@aws-sdk/types': 3.973.5 - '@smithy/credential-provider-imds': 4.2.12 - '@smithy/property-provider': 4.2.12 - '@smithy/shared-ini-file-loader': 4.4.7 - '@smithy/types': 4.13.1 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/credential-provider-ini@3.972.20': dependencies: '@aws-sdk/core': 3.973.20 @@ -3950,19 +3759,6 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-login@3.972.18': - dependencies: - '@aws-sdk/core': 3.973.19 - '@aws-sdk/nested-clients': 3.996.8 - '@aws-sdk/types': 3.973.5 - '@smithy/property-provider': 4.2.12 - '@smithy/protocol-http': 5.3.12 - '@smithy/shared-ini-file-loader': 4.4.7 - '@smithy/types': 4.13.1 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/credential-provider-login@3.972.20': dependencies: '@aws-sdk/core': 3.973.20 @@ -3976,23 +3772,6 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-node@3.972.19': - dependencies: - '@aws-sdk/credential-provider-env': 3.972.17 - '@aws-sdk/credential-provider-http': 3.972.19 - '@aws-sdk/credential-provider-ini': 3.972.18 - '@aws-sdk/credential-provider-process': 3.972.17 - '@aws-sdk/credential-provider-sso': 3.972.18 - '@aws-sdk/credential-provider-web-identity': 3.972.18 - '@aws-sdk/types': 3.973.5 - '@smithy/credential-provider-imds': 4.2.12 - '@smithy/property-provider': 4.2.12 - '@smithy/shared-ini-file-loader': 4.4.7 - '@smithy/types': 4.13.1 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/credential-provider-node@3.972.21': dependencies: '@aws-sdk/credential-provider-env': 3.972.18 @@ -4010,15 +3789,6 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-process@3.972.17': - dependencies: - '@aws-sdk/core': 3.973.19 - '@aws-sdk/types': 3.973.5 - '@smithy/property-provider': 4.2.12 - '@smithy/shared-ini-file-loader': 4.4.7 - '@smithy/types': 4.13.1 - tslib: 2.8.1 - '@aws-sdk/credential-provider-process@3.972.18': dependencies: '@aws-sdk/core': 3.973.20 @@ -4028,19 +3798,6 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/credential-provider-sso@3.972.18': - dependencies: - '@aws-sdk/core': 3.973.19 - '@aws-sdk/nested-clients': 3.996.8 - '@aws-sdk/token-providers': 3.1005.0 - '@aws-sdk/types': 3.973.5 - '@smithy/property-provider': 4.2.12 - '@smithy/shared-ini-file-loader': 4.4.7 - '@smithy/types': 4.13.1 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/credential-provider-sso@3.972.20': dependencies: '@aws-sdk/core': 3.973.20 @@ -4054,18 +3811,6 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-web-identity@3.972.18': - dependencies: - '@aws-sdk/core': 3.973.19 - '@aws-sdk/nested-clients': 3.996.8 - '@aws-sdk/types': 3.973.5 - '@smithy/property-provider': 4.2.12 - '@smithy/shared-ini-file-loader': 4.4.7 - '@smithy/types': 4.13.1 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/credential-provider-web-identity@3.972.20': dependencies: '@aws-sdk/core': 3.973.20 @@ -4080,21 +3825,14 @@ snapshots: '@aws-sdk/eventstream-handler-node@3.972.10': dependencies: - '@aws-sdk/types': 3.973.5 + '@aws-sdk/types': 3.973.6 '@smithy/eventstream-codec': 4.2.12 '@smithy/types': 4.13.1 tslib: 2.8.1 '@aws-sdk/middleware-eventstream@3.972.7': dependencies: - '@aws-sdk/types': 3.973.5 - '@smithy/protocol-http': 5.3.12 - '@smithy/types': 4.13.1 - tslib: 2.8.1 - - '@aws-sdk/middleware-host-header@3.972.7': - dependencies: - '@aws-sdk/types': 3.973.5 + '@aws-sdk/types': 3.973.6 '@smithy/protocol-http': 5.3.12 '@smithy/types': 4.13.1 tslib: 2.8.1 @@ -4106,26 +3844,12 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/middleware-logger@3.972.7': - dependencies: - '@aws-sdk/types': 3.973.5 - '@smithy/types': 4.13.1 - tslib: 2.8.1 - '@aws-sdk/middleware-logger@3.972.8': dependencies: '@aws-sdk/types': 3.973.6 '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/middleware-recursion-detection@3.972.7': - dependencies: - '@aws-sdk/types': 3.973.5 - '@aws/lambda-invoke-store': 0.2.3 - '@smithy/protocol-http': 5.3.12 - '@smithy/types': 4.13.1 - tslib: 2.8.1 - '@aws-sdk/middleware-recursion-detection@3.972.8': dependencies: '@aws-sdk/types': 3.973.6 @@ -4134,17 +3858,6 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/middleware-user-agent@3.972.20': - dependencies: - '@aws-sdk/core': 3.973.19 - '@aws-sdk/types': 3.973.5 - '@aws-sdk/util-endpoints': 3.996.4 - '@smithy/core': 3.23.10 - '@smithy/protocol-http': 5.3.12 - '@smithy/types': 4.13.1 - '@smithy/util-retry': 4.2.12 - tslib: 2.8.1 - '@aws-sdk/middleware-user-agent@3.972.21': dependencies: '@aws-sdk/core': 3.973.20 @@ -4158,11 +3871,11 @@ snapshots: '@aws-sdk/middleware-websocket@3.972.12': dependencies: - '@aws-sdk/types': 3.973.5 + '@aws-sdk/types': 3.973.6 '@aws-sdk/util-format-url': 3.972.7 '@smithy/eventstream-codec': 4.2.12 '@smithy/eventstream-serde-browser': 4.2.12 - '@smithy/fetch-http-handler': 5.3.14 + '@smithy/fetch-http-handler': 5.3.15 '@smithy/protocol-http': 5.3.12 '@smithy/signature-v4': 5.3.12 '@smithy/types': 4.13.1 @@ -4214,57 +3927,6 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/nested-clients@3.996.8': - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.973.19 - '@aws-sdk/middleware-host-header': 3.972.7 - '@aws-sdk/middleware-logger': 3.972.7 - '@aws-sdk/middleware-recursion-detection': 3.972.7 - '@aws-sdk/middleware-user-agent': 3.972.20 - '@aws-sdk/region-config-resolver': 3.972.7 - '@aws-sdk/types': 3.973.5 - '@aws-sdk/util-endpoints': 3.996.4 - '@aws-sdk/util-user-agent-browser': 3.972.7 - '@aws-sdk/util-user-agent-node': 3.973.5 - '@smithy/config-resolver': 4.4.11 - '@smithy/core': 3.23.10 - '@smithy/fetch-http-handler': 5.3.14 - '@smithy/hash-node': 4.2.12 - '@smithy/invalid-dependency': 4.2.12 - '@smithy/middleware-content-length': 4.2.12 - '@smithy/middleware-endpoint': 4.4.24 - '@smithy/middleware-retry': 4.4.41 - '@smithy/middleware-serde': 4.2.13 - '@smithy/middleware-stack': 4.2.12 - '@smithy/node-config-provider': 4.3.12 - '@smithy/node-http-handler': 4.4.15 - '@smithy/protocol-http': 5.3.12 - '@smithy/smithy-client': 4.12.4 - '@smithy/types': 4.13.1 - '@smithy/url-parser': 4.2.12 - '@smithy/util-base64': 4.3.2 - '@smithy/util-body-length-browser': 4.2.2 - '@smithy/util-body-length-node': 4.2.3 - '@smithy/util-defaults-mode-browser': 4.3.40 - '@smithy/util-defaults-mode-node': 4.2.43 - '@smithy/util-endpoints': 3.3.3 - '@smithy/util-middleware': 4.2.12 - '@smithy/util-retry': 4.2.12 - '@smithy/util-utf8': 4.2.2 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/region-config-resolver@3.972.7': - dependencies: - '@aws-sdk/types': 3.973.5 - '@smithy/config-resolver': 4.4.11 - '@smithy/node-config-provider': 4.3.12 - '@smithy/types': 4.13.1 - tslib: 2.8.1 - '@aws-sdk/region-config-resolver@3.972.8': dependencies: '@aws-sdk/types': 3.973.6 @@ -4273,23 +3935,11 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/token-providers@3.1005.0': - dependencies: - '@aws-sdk/core': 3.973.19 - '@aws-sdk/nested-clients': 3.996.8 - '@aws-sdk/types': 3.973.5 - '@smithy/property-provider': 4.2.12 - '@smithy/shared-ini-file-loader': 4.4.7 - '@smithy/types': 4.13.1 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/token-providers@3.1007.0': dependencies: - '@aws-sdk/core': 3.973.19 - '@aws-sdk/nested-clients': 3.996.8 - '@aws-sdk/types': 3.973.5 + '@aws-sdk/core': 3.973.20 + '@aws-sdk/nested-clients': 3.996.10 + '@aws-sdk/types': 3.973.6 '@smithy/property-provider': 4.2.12 '@smithy/shared-ini-file-loader': 4.4.7 '@smithy/types': 4.13.1 @@ -4309,24 +3959,11 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/types@3.973.5': - dependencies: - '@smithy/types': 4.13.1 - tslib: 2.8.1 - '@aws-sdk/types@3.973.6': dependencies: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/util-endpoints@3.996.4': - dependencies: - '@aws-sdk/types': 3.973.5 - '@smithy/types': 4.13.1 - '@smithy/url-parser': 4.2.12 - '@smithy/util-endpoints': 3.3.3 - tslib: 2.8.1 - '@aws-sdk/util-endpoints@3.996.5': dependencies: '@aws-sdk/types': 3.973.6 @@ -4337,7 +3974,7 @@ snapshots: '@aws-sdk/util-format-url@3.972.7': dependencies: - '@aws-sdk/types': 3.973.5 + '@aws-sdk/types': 3.973.6 '@smithy/querystring-builder': 4.2.12 '@smithy/types': 4.13.1 tslib: 2.8.1 @@ -4346,13 +3983,6 @@ snapshots: dependencies: tslib: 2.8.1 - '@aws-sdk/util-user-agent-browser@3.972.7': - dependencies: - '@aws-sdk/types': 3.973.5 - '@smithy/types': 4.13.1 - bowser: 2.14.1 - tslib: 2.8.1 - '@aws-sdk/util-user-agent-browser@3.972.8': dependencies: '@aws-sdk/types': 3.973.6 @@ -4360,14 +3990,6 @@ snapshots: bowser: 2.14.1 tslib: 2.8.1 - '@aws-sdk/util-user-agent-node@3.973.5': - dependencies: - '@aws-sdk/middleware-user-agent': 3.972.20 - '@aws-sdk/types': 3.973.5 - '@smithy/node-config-provider': 4.3.12 - '@smithy/types': 4.13.1 - tslib: 2.8.1 - '@aws-sdk/util-user-agent-node@3.973.7': dependencies: '@aws-sdk/middleware-user-agent': 3.972.21 @@ -4377,12 +3999,6 @@ snapshots: '@smithy/util-config-provider': 4.2.2 tslib: 2.8.1 - '@aws-sdk/xml-builder@3.972.10': - dependencies: - '@smithy/types': 4.13.1 - fast-xml-parser: 5.4.1 - tslib: 2.8.1 - '@aws-sdk/xml-builder@3.972.11': dependencies: '@smithy/types': 4.13.1 @@ -4845,9 +4461,9 @@ snapshots: std-env: 3.10.0 yoctocolors: 2.1.2 - '@mariozechner/pi-agent-core@0.57.1(@modelcontextprotocol/sdk@1.27.1(zod@4.3.6))(ws@8.19.0)(zod@4.3.6)': + '@mariozechner/pi-agent-core@0.58.0(@modelcontextprotocol/sdk@1.27.1(zod@4.3.6))(ws@8.19.0)(zod@4.3.6)': dependencies: - '@mariozechner/pi-ai': 0.57.1(@modelcontextprotocol/sdk@1.27.1(zod@4.3.6))(ws@8.19.0)(zod@4.3.6) + '@mariozechner/pi-ai': 0.58.0(@modelcontextprotocol/sdk@1.27.1(zod@4.3.6))(ws@8.19.0)(zod@4.3.6) transitivePeerDependencies: - '@modelcontextprotocol/sdk' - aws-crt @@ -4857,7 +4473,7 @@ snapshots: - ws - zod - '@mariozechner/pi-ai@0.57.1(@modelcontextprotocol/sdk@1.27.1(zod@4.3.6))(ws@8.19.0)(zod@4.3.6)': + '@mariozechner/pi-ai@0.58.0(@modelcontextprotocol/sdk@1.27.1(zod@4.3.6))(ws@8.19.0)(zod@4.3.6)': dependencies: '@anthropic-ai/sdk': 0.73.0(zod@4.3.6) '@aws-sdk/client-bedrock-runtime': 3.1007.0 @@ -4881,12 +4497,12 @@ snapshots: - ws - zod - '@mariozechner/pi-coding-agent@0.57.1(@modelcontextprotocol/sdk@1.27.1(zod@4.3.6))(ws@8.19.0)(zod@4.3.6)': + '@mariozechner/pi-coding-agent@0.58.0(@modelcontextprotocol/sdk@1.27.1(zod@4.3.6))(ws@8.19.0)(zod@4.3.6)': dependencies: '@mariozechner/jiti': 2.6.5 - '@mariozechner/pi-agent-core': 0.57.1(@modelcontextprotocol/sdk@1.27.1(zod@4.3.6))(ws@8.19.0)(zod@4.3.6) - '@mariozechner/pi-ai': 0.57.1(@modelcontextprotocol/sdk@1.27.1(zod@4.3.6))(ws@8.19.0)(zod@4.3.6) - '@mariozechner/pi-tui': 0.57.1 + '@mariozechner/pi-agent-core': 0.58.0(@modelcontextprotocol/sdk@1.27.1(zod@4.3.6))(ws@8.19.0)(zod@4.3.6) + '@mariozechner/pi-ai': 0.58.0(@modelcontextprotocol/sdk@1.27.1(zod@4.3.6))(ws@8.19.0)(zod@4.3.6) + '@mariozechner/pi-tui': 0.58.0 '@silvia-odwyer/photon-node': 0.3.4 chalk: 5.6.2 cli-highlight: 2.1.11 @@ -4913,7 +4529,7 @@ snapshots: - ws - zod - '@mariozechner/pi-tui@0.57.1': + '@mariozechner/pi-tui@0.58.0': dependencies: '@types/mime-types': 2.1.4 chalk: 5.6.2 @@ -5367,10 +4983,10 @@ snapshots: '@slack/bolt@4.6.0(@types/express@5.0.6)': dependencies: - '@slack/logger': 4.0.0 + '@slack/logger': 4.0.1 '@slack/oauth': 3.0.4 '@slack/socket-mode': 2.0.5 - '@slack/types': 2.20.0 + '@slack/types': 2.20.1 '@slack/web-api': 7.15.0 '@types/express': 5.0.6 axios: 1.13.6 @@ -5384,17 +5000,13 @@ snapshots: - supports-color - utf-8-validate - '@slack/logger@4.0.0': - dependencies: - '@types/node': 24.12.0 - '@slack/logger@4.0.1': dependencies: '@types/node': 24.12.0 '@slack/oauth@3.0.4': dependencies: - '@slack/logger': 4.0.0 + '@slack/logger': 4.0.1 '@slack/web-api': 7.15.0 '@types/jsonwebtoken': 9.0.10 '@types/node': 24.12.0 @@ -5404,7 +5016,7 @@ snapshots: '@slack/socket-mode@2.0.5': dependencies: - '@slack/logger': 4.0.0 + '@slack/logger': 4.0.1 '@slack/web-api': 7.15.0 '@types/node': 24.12.0 '@types/ws': 8.18.1 @@ -5415,8 +5027,6 @@ snapshots: - debug - utf-8-validate - '@slack/types@2.20.0': {} - '@slack/types@2.20.1': {} '@slack/web-api@7.15.0': @@ -5450,19 +5060,6 @@ snapshots: '@smithy/util-middleware': 4.2.12 tslib: 2.8.1 - '@smithy/core@3.23.10': - dependencies: - '@smithy/protocol-http': 5.3.12 - '@smithy/types': 4.13.1 - '@smithy/url-parser': 4.2.12 - '@smithy/util-base64': 4.3.2 - '@smithy/util-body-length-browser': 4.2.2 - '@smithy/util-middleware': 4.2.12 - '@smithy/util-stream': 4.5.18 - '@smithy/util-utf8': 4.2.2 - '@smithy/uuid': 1.1.2 - tslib: 2.8.1 - '@smithy/core@3.23.11': dependencies: '@smithy/protocol-http': 5.3.12 @@ -5514,14 +5111,6 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/fetch-http-handler@5.3.14': - dependencies: - '@smithy/protocol-http': 5.3.12 - '@smithy/querystring-builder': 4.2.12 - '@smithy/types': 4.13.1 - '@smithy/util-base64': 4.3.2 - tslib: 2.8.1 - '@smithy/fetch-http-handler@5.3.15': dependencies: '@smithy/protocol-http': 5.3.12 @@ -5556,17 +5145,6 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/middleware-endpoint@4.4.24': - dependencies: - '@smithy/core': 3.23.10 - '@smithy/middleware-serde': 4.2.13 - '@smithy/node-config-provider': 4.3.12 - '@smithy/shared-ini-file-loader': 4.4.7 - '@smithy/types': 4.13.1 - '@smithy/url-parser': 4.2.12 - '@smithy/util-middleware': 4.2.12 - tslib: 2.8.1 - '@smithy/middleware-endpoint@4.4.25': dependencies: '@smithy/core': 3.23.11 @@ -5578,18 +5156,6 @@ snapshots: '@smithy/util-middleware': 4.2.12 tslib: 2.8.1 - '@smithy/middleware-retry@4.4.41': - dependencies: - '@smithy/node-config-provider': 4.3.12 - '@smithy/protocol-http': 5.3.12 - '@smithy/service-error-classification': 4.2.12 - '@smithy/smithy-client': 4.12.4 - '@smithy/types': 4.13.1 - '@smithy/util-middleware': 4.2.12 - '@smithy/util-retry': 4.2.12 - '@smithy/uuid': 1.1.2 - tslib: 2.8.1 - '@smithy/middleware-retry@4.4.42': dependencies: '@smithy/node-config-provider': 4.3.12 @@ -5602,13 +5168,6 @@ snapshots: '@smithy/uuid': 1.1.2 tslib: 2.8.1 - '@smithy/middleware-serde@4.2.13': - dependencies: - '@smithy/core': 3.23.10 - '@smithy/protocol-http': 5.3.12 - '@smithy/types': 4.13.1 - tslib: 2.8.1 - '@smithy/middleware-serde@4.2.14': dependencies: '@smithy/core': 3.23.11 @@ -5628,14 +5187,6 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/node-http-handler@4.4.15': - dependencies: - '@smithy/abort-controller': 4.2.12 - '@smithy/protocol-http': 5.3.12 - '@smithy/querystring-builder': 4.2.12 - '@smithy/types': 4.13.1 - tslib: 2.8.1 - '@smithy/node-http-handler@4.4.16': dependencies: '@smithy/abort-controller': 4.2.12 @@ -5685,16 +5236,6 @@ snapshots: '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - '@smithy/smithy-client@4.12.4': - dependencies: - '@smithy/core': 3.23.10 - '@smithy/middleware-endpoint': 4.4.24 - '@smithy/middleware-stack': 4.2.12 - '@smithy/protocol-http': 5.3.12 - '@smithy/types': 4.13.1 - '@smithy/util-stream': 4.5.18 - tslib: 2.8.1 - '@smithy/smithy-client@4.12.5': dependencies: '@smithy/core': 3.23.11 @@ -5743,13 +5284,6 @@ snapshots: dependencies: tslib: 2.8.1 - '@smithy/util-defaults-mode-browser@4.3.40': - dependencies: - '@smithy/property-provider': 4.2.12 - '@smithy/smithy-client': 4.12.4 - '@smithy/types': 4.13.1 - tslib: 2.8.1 - '@smithy/util-defaults-mode-browser@4.3.41': dependencies: '@smithy/property-provider': 4.2.12 @@ -5757,16 +5291,6 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/util-defaults-mode-node@4.2.43': - dependencies: - '@smithy/config-resolver': 4.4.11 - '@smithy/credential-provider-imds': 4.2.12 - '@smithy/node-config-provider': 4.3.12 - '@smithy/property-provider': 4.2.12 - '@smithy/smithy-client': 4.12.4 - '@smithy/types': 4.13.1 - tslib: 2.8.1 - '@smithy/util-defaults-mode-node@4.2.44': dependencies: '@smithy/config-resolver': 4.4.11 @@ -5798,17 +5322,6 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/util-stream@4.5.18': - dependencies: - '@smithy/fetch-http-handler': 5.3.14 - '@smithy/node-http-handler': 4.4.15 - '@smithy/types': 4.13.1 - '@smithy/util-base64': 4.3.2 - '@smithy/util-buffer-from': 4.2.2 - '@smithy/util-hex-encoding': 4.2.2 - '@smithy/util-utf8': 4.2.2 - tslib: 2.8.1 - '@smithy/util-stream@4.5.19': dependencies: '@smithy/fetch-http-handler': 5.3.15 @@ -7233,7 +6746,7 @@ snapshots: ws: 8.19.0 zod: 4.3.6 - openclaw@file:../../.codex/worktrees/cb00/openclaw(@napi-rs/canvas@0.1.96)(@types/express@5.0.6)(node-llama-cpp@3.16.2(typescript@5.9.3)): + openclaw@2026.3.13(@napi-rs/canvas@0.1.96)(@types/express@5.0.6)(node-llama-cpp@3.16.2(typescript@5.9.3)): dependencies: '@agentclientprotocol/sdk': 0.16.1(zod@4.3.6) '@aws-sdk/client-bedrock': 3.1009.0 @@ -7246,10 +6759,10 @@ snapshots: '@larksuiteoapi/node-sdk': 1.59.0 '@line/bot-sdk': 10.6.0 '@lydell/node-pty': 1.2.0-beta.3 - '@mariozechner/pi-agent-core': 0.57.1(@modelcontextprotocol/sdk@1.27.1(zod@4.3.6))(ws@8.19.0)(zod@4.3.6) - '@mariozechner/pi-ai': 0.57.1(@modelcontextprotocol/sdk@1.27.1(zod@4.3.6))(ws@8.19.0)(zod@4.3.6) - '@mariozechner/pi-coding-agent': 0.57.1(@modelcontextprotocol/sdk@1.27.1(zod@4.3.6))(ws@8.19.0)(zod@4.3.6) - '@mariozechner/pi-tui': 0.57.1 + '@mariozechner/pi-agent-core': 0.58.0(@modelcontextprotocol/sdk@1.27.1(zod@4.3.6))(ws@8.19.0)(zod@4.3.6) + '@mariozechner/pi-ai': 0.58.0(@modelcontextprotocol/sdk@1.27.1(zod@4.3.6))(ws@8.19.0)(zod@4.3.6) + '@mariozechner/pi-coding-agent': 0.58.0(@modelcontextprotocol/sdk@1.27.1(zod@4.3.6))(ws@8.19.0)(zod@4.3.6) + '@mariozechner/pi-tui': 0.58.0 '@modelcontextprotocol/sdk': 1.27.1(zod@4.3.6) '@mozilla/readability': 0.6.0 '@napi-rs/canvas': 0.1.96 diff --git a/scripts/apply-release-version.mjs b/scripts/apply-release-version.mjs new file mode 100644 index 0000000..b36deaa --- /dev/null +++ b/scripts/apply-release-version.mjs @@ -0,0 +1,35 @@ +import { readFileSync, writeFileSync } from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const workspaceRoot = path.dirname(path.dirname(fileURLToPath(import.meta.url))); +const version = parseReleaseVersion(process.argv[2]); + +const packageJsonPath = path.join(workspaceRoot, "package.json"); +const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8")); +packageJson.version = version; +writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`); +process.stdout.write(`updated package.json -> ${version}\n`); + +const clientPath = path.join(workspaceRoot, "src/client.ts"); +const clientSource = readFileSync(clientPath, "utf8"); +const updatedClientSource = clientSource.replace( + /clientInfo: \{ name: "openclaw-codex-app-server", version: "[^"]+" \}/, + `clientInfo: { name: "openclaw-codex-app-server", version: "${version}" }`, +); +if (updatedClientSource === clientSource) { + throw new Error("Could not update clientInfo version in src/client.ts"); +} +writeFileSync(clientPath, updatedClientSource); +process.stdout.write(`updated src/client.ts -> ${version}\n`); + +function parseReleaseVersion(tagName) { + if (!tagName) { + throw new Error("Missing release tag. Expected vX.Y.Z"); + } + const match = tagName.trim().match(/^v(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)$/); + if (!match) { + throw new Error(`Invalid release tag: ${tagName}. Expected vX.Y.Z`); + } + return match[1]; +} diff --git a/scripts/pack-smoke.mjs b/scripts/pack-smoke.mjs new file mode 100644 index 0000000..f785441 --- /dev/null +++ b/scripts/pack-smoke.mjs @@ -0,0 +1,82 @@ +import { execFileSync } from "node:child_process"; +import { existsSync, mkdtempSync, mkdirSync, readdirSync, rmSync, writeFileSync } from "node:fs"; +import os from "node:os"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const workspaceRoot = path.dirname(path.dirname(fileURLToPath(import.meta.url))); +const tempRoot = mkdtempSync(path.join(os.tmpdir(), "openclaw-codex-pack-smoke-")); +const tarballDir = path.join(tempRoot, "tarballs"); +const installDir = path.join(tempRoot, "install"); + +mkdirSync(tarballDir, { recursive: true }); +mkdirSync(installDir, { recursive: true }); + +try { + exec("pnpm", ["pack", "--pack-destination", tarballDir]); + + const tarballs = readdirSync(tarballDir) + .filter((entry) => entry.endsWith(".tgz")) + .sort() + .map((entry) => path.join(tarballDir, entry)); + + if (tarballs.length !== 1) { + throw new Error(`Expected exactly one tarball, found ${tarballs.length}`); + } + + writeFileSync( + path.join(installDir, "package.json"), + `${JSON.stringify( + { + name: "openclaw-codex-app-server-pack-smoke", + private: true, + version: "0.0.0", + packageManager: "npm@10.9.2", + }, + null, + 2, + )}\n`, + ); + + exec("npm", ["install", "--no-package-lock", tarballs[0]], installDir); + + const packageDir = path.join(installDir, "node_modules", "openclaw-codex-app-server"); + const expectedFiles = [ + "package.json", + "README.md", + "LICENSE", + "index.ts", + "openclaw.plugin.json", + path.join("src", "client.ts"), + ]; + const unexpectedFiles = [ + "AGENTS.md", + "OVERNIGHT-TODO.md", + path.join("src", "client.test.ts"), + path.join("src", "controller.test.ts"), + ]; + + for (const relativePath of expectedFiles) { + if (!existsSync(path.join(packageDir, relativePath))) { + throw new Error(`Missing expected published file: ${relativePath}`); + } + } + + for (const relativePath of unexpectedFiles) { + if (existsSync(path.join(packageDir, relativePath))) { + throw new Error(`Found unexpected published file: ${relativePath}`); + } + } + + process.stdout.write(`pack smoke ok (${tempRoot})\n`); +} finally { + rmSync(tempRoot, { recursive: true, force: true }); +} + +function exec(command, args, cwd = workspaceRoot) { + execFileSync(command, args, { + cwd, + stdio: "inherit", + env: process.env, + }); +} diff --git a/src/client.ts b/src/client.ts index c5bc711..459b8fe 100644 --- a/src/client.ts +++ b/src/client.ts @@ -733,7 +733,7 @@ async function initializeClient(params: { }): Promise { await params.client.request("initialize", { protocolVersion: DEFAULT_PROTOCOL_VERSION, - clientInfo: { name: "openclaw-codex-app-server", version: "0.0.0-development" }, + clientInfo: { name: "openclaw-codex-app-server", version: "2026.3.13" }, capabilities: { experimentalApi: true }, }); await params.client.notify("initialized", {}); diff --git a/src/format.test.ts b/src/format.test.ts index d25a91f..b583c2e 100644 --- a/src/format.test.ts +++ b/src/format.test.ts @@ -1,3 +1,4 @@ +import os from "node:os"; import { afterEach, describe, expect, it, vi } from "vitest"; import { buildCodexPlanMarkdownPreview, @@ -8,6 +9,7 @@ import { formatCodexPlanSteps, formatCodexReviewFindingMessage, formatCodexStatusText, + getCodexStatusTimeZoneLabel, formatMcpServers, formatModels, formatSkills, @@ -16,6 +18,17 @@ import { parseCodexReviewOutput, } from "./format.js"; +function shortenHomePathForTest(value: string): string { + const home = os.homedir(); + if (value === home) { + return "~"; + } + if (value.startsWith(`${home}/`)) { + return `~/${value.slice(home.length + 1)}`; + } + return value; +} + describe("formatThreadButtonLabel", () => { it("uses worktree and age badges while keeping the project suffix at the end", () => { expect( @@ -124,8 +137,12 @@ describe("formatCodexStatusText", () => { expect(text).toContain("Binding: active"); expect(text).toContain("Thread: Fix Telegram approval flow"); expect(text).toContain("Model: openai/gpt-5.4 ยท reasoning high"); - expect(text).toContain("Project folder: ~/github/openclaw"); - expect(text).toContain("Worktree folder: ~/.codex/worktrees/41fb/openclaw"); + expect(text).toContain( + `Project folder: ${shortenHomePathForTest("/Users/huntharo/github/openclaw")}`, + ); + expect(text).toContain( + `Worktree folder: ${shortenHomePathForTest("/Users/huntharo/.codex/worktrees/41fb/openclaw")}`, + ); expect(text).toContain("Fast mode: off"); expect(text).toContain("Plan mode: off"); expect(text).toContain("Context usage: unavailable until Codex emits a token-usage update"); @@ -344,10 +361,13 @@ describe("formatCodexStatusText", () => { ], }); - expect(text).toContain( - `Rate limits timezone: ${new Intl.DateTimeFormat().resolvedOptions().timeZone}`, - ); - expect(text).toContain("5h limit: 89% left (resets 12:28 PM)"); + const expectedFiveHourReset = new Intl.DateTimeFormat(undefined, { + hour: "numeric", + minute: "2-digit", + }).format(new Date("2026-03-07T17:28:00Z")); + + expect(text).toContain(`Rate limits timezone: ${getCodexStatusTimeZoneLabel()}`); + expect(text).toContain(`5h limit: 89% left (resets ${expectedFiveHourReset})`); expect(text).toContain("Weekly limit: 80% left (resets Mar 11)"); expect(text).not.toContain("Jan 21"); });