Compare commits
52 Commits
fix/sectio
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aaf0df956f | ||
|
|
127687134a | ||
|
|
b4bf554c43 | ||
|
|
8bbe9a4532 | ||
|
|
926afe32ab | ||
|
|
e447e9c534 | ||
|
|
4955f7ffb8 | ||
|
|
886c9ac60a | ||
|
|
f3a37af6dd | ||
|
|
0012d40688 | ||
|
|
983d42f9d6 | ||
|
|
8d8043c587 | ||
|
|
b31cbea7a0 | ||
|
|
0f6d639711 | ||
|
|
c38308dee0 | ||
|
|
7f96cf49f8 | ||
|
|
48a1c6db5a | ||
|
|
b81a86617a | ||
|
|
eaec92c010 | ||
|
|
5cd0ffdecd | ||
|
|
1c571d990a | ||
|
|
061e028153 | ||
|
|
81e0c091f6 | ||
|
|
39af77fadb | ||
|
|
8a6a63a6ae | ||
|
|
1b66734d68 | ||
|
|
5766da5653 | ||
|
|
91584d25e4 | ||
|
|
2da667ea5b | ||
|
|
7694ee44ad | ||
|
|
236c7a9d70 | ||
|
|
cf30a8aac5 | ||
|
|
ddc6c56e83 | ||
|
|
d99fcca22c | ||
|
|
e7a1a4238e | ||
|
|
076b5531d6 | ||
|
|
713c4471eb | ||
|
|
d61b65d135 | ||
|
|
0a9a44214a | ||
|
|
8bb25ddd49 | ||
|
|
4fd2161d55 | ||
|
|
74121a8a80 | ||
|
|
ce605576c8 | ||
|
|
662996827a | ||
|
|
921b5835ad | ||
|
|
599ea88850 | ||
|
|
6b735382f1 | ||
|
|
4ca1074b98 | ||
|
|
b4fe8c067b | ||
|
|
b141735c4a | ||
|
|
5bc2a2a95b | ||
|
|
a32a6f7cad |
13
.github/workflows/deploy.yml
vendored
13
.github/workflows/deploy.yml
vendored
@ -17,10 +17,15 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: 24
|
||||
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@ecf28ddc73e819eb6fa29df6b34ef8921c743461 # v2
|
||||
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
|
||||
with:
|
||||
bun-version: latest
|
||||
|
||||
@ -31,7 +36,7 @@ jobs:
|
||||
run: bun run build
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v3
|
||||
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
|
||||
with:
|
||||
path: ./dist
|
||||
|
||||
@ -44,4 +49,4 @@ jobs:
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4
|
||||
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0
|
||||
|
||||
4
.github/workflows/install-git-smoke.yml
vendored
4
.github/workflows/install-git-smoke.yml
vendored
@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout installer
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Build git smoke image
|
||||
run: docker build -t clawdbot-install-git-smoke:ci -f scripts/docker/install-sh-git-smoke/Dockerfile .
|
||||
@ -25,7 +25,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout installer
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Build git smoke image (install-cli.sh)
|
||||
run: docker build -t openclaw-install-cli-git-smoke:ci -f scripts/docker/install-cli-git-smoke/Dockerfile .
|
||||
|
||||
6
.github/workflows/install-matrix.yml
vendored
6
.github/workflows/install-matrix.yml
vendored
@ -1,5 +1,9 @@
|
||||
name: Install Matrix
|
||||
|
||||
concurrency:
|
||||
group: install-matrix-${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
@ -29,7 +33,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Run matrix (push/pr)
|
||||
if: github.event_name != 'workflow_dispatch'
|
||||
|
||||
38
.github/workflows/install-smoke.yml
vendored
38
.github/workflows/install-smoke.yml
vendored
@ -1,5 +1,9 @@
|
||||
name: Install Smoke
|
||||
|
||||
concurrency:
|
||||
group: install-smoke-${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
@ -11,10 +15,10 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: install.sh dry run
|
||||
if: runner.os != 'Windows'
|
||||
@ -30,11 +34,24 @@ jobs:
|
||||
shell: cmd
|
||||
run: set "CLAWDBOT_INSTALL_PS1_URL=%GITHUB_WORKSPACE%\\public\\install.ps1" && .\\public\\install.cmd --dry-run --no-onboard --npm
|
||||
|
||||
install-scripts-macos:
|
||||
# GitHub-hosted macOS runners are frequently capacity constrained.
|
||||
# Keep this check available for manual verification without blocking
|
||||
# normal push/PR CI on an unstarted job.
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: install.sh dry run
|
||||
run: bash public/install.sh --dry-run --no-onboard --no-prompt
|
||||
|
||||
shellcheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Install ShellCheck
|
||||
run: sudo apt-get update -y && sudo apt-get install -y shellcheck
|
||||
@ -46,16 +63,19 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Unit tests (install.sh)
|
||||
run: bash scripts/test-install-sh-unit.sh
|
||||
|
||||
- name: Unit tests (install-cli.sh)
|
||||
run: bash scripts/test-install-cli-unit.sh
|
||||
|
||||
install-smoke:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout installer
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Build smoke image (root)
|
||||
run: docker build -t clawdbot-install-smoke:ci -f scripts/docker/install-sh-smoke/Dockerfile .
|
||||
@ -80,7 +100,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout installer
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Build git smoke image (install.sh)
|
||||
run: docker build -t clawdbot-install-git-smoke:ci -f scripts/docker/install-sh-git-smoke/Dockerfile .
|
||||
@ -95,7 +115,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout installer
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Build git smoke image (install-cli.sh)
|
||||
run: docker build -t openclaw-install-cli-git-smoke:ci -f scripts/docker/install-cli-git-smoke/Dockerfile .
|
||||
@ -112,10 +132,10 @@ jobs:
|
||||
node: [22, 24]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
|
||||
|
||||
17
CHANGELOG.md
17
CHANGELOG.md
@ -1,5 +1,20 @@
|
||||
# Changelog
|
||||
|
||||
## 2026-04-26
|
||||
|
||||
- Installer: normalize `HOME` when headless VM execution reports `/`, preventing npm from trying to use `/.npm` on macOS Parallels guests.
|
||||
- Installer: keep `install-cli.sh` package installs under the prefix-local Node toolchain so `openclaw update` does not create a second package root (#118, thanks @AISymbiote).
|
||||
- Integrations: add QQ Bot to the chat provider list with the canonical docs link (#119, thanks @sliverp).
|
||||
- Integrations: point Notion, Bear Notes, GitHub, Image Gen, and Camera cards to their specific ClawHub skill pages (#117, thanks @DJStompZone).
|
||||
- Dependencies: update Astro to 6.1.9, migrate blog content to Astro 6 content collections, refresh RSS/icons/analytics packages, and bump pinned GitHub Actions.
|
||||
- Windows installer: route PowerShell install failures through a top-level handler so `irm ... | iex` returns control to the current shell while direct script-file runs still exit non-zero. Fixes openclaw/openclaw#38054, thanks @PwrSrg.
|
||||
- Installer: warn when multiple npm global roots contain OpenClaw installs, showing active Node/npm/openclaw plus each install path and version so stale version-manager installs are visible. Fixes openclaw/openclaw#40839, thanks @zhixianio.
|
||||
|
||||
## 2026-03-16
|
||||
|
||||
- Integrations: correct MS Teams docs link to the canonical `/channels/msteams` path (#109, thanks @SidU).
|
||||
- Showcase/press: add curated community builds and a press page, and surface standout examples on the homepage (#108, thanks @jchopard69).
|
||||
|
||||
## 2026-03-07
|
||||
|
||||
- Shoutouts: use the theme-aware card surface token so shoutout cards render correctly in light mode (#100, thanks @zwying0814).
|
||||
@ -9,7 +24,7 @@
|
||||
- Dependencies: bump `@lucide/astro` to `0.577.0` and sync `bun.lock` (#99, thanks @dependabot).
|
||||
- CI: update Bun setup pin and move the install-smoke Node setup to the pinned `actions/setup-node` v6 SHA (#98, thanks @dependabot).
|
||||
- Installer: recover from older PATH-bound Node runtimes after install, but keep the fallback `openclaw` shim in `~/.local/bin` instead of mutating version-manager bins (#68, thanks @rolandkakonyi).
|
||||
- UI: extract a shared homepage section-header component to keep spacing and link treatment consistent (#62, thanks @luiginotmario).
|
||||
- Dependencies: update `astro` to `5.18.0` and `simple-icons` to `16.10.0`; add workflow concurrency so stale install jobs on `main` cancel instead of queueing indefinitely.
|
||||
## 2026-02-22
|
||||
|
||||
- Installer: make gum behavior fully automatic (interactive TTYs get gum, headless shells get plain status), and remove manual gum toggles.
|
||||
|
||||
@ -1,229 +0,0 @@
|
||||
# Code Optimizations Summary
|
||||
|
||||
This document summarizes the technical improvements made to the OpenClaw landing page codebase.
|
||||
|
||||
## Changes Made
|
||||
|
||||
### ✅ 1. Fixed Deprecated API Usage
|
||||
**File**: `src/pages/index.astro` (Line ~320)
|
||||
|
||||
**Before**:
|
||||
```javascript
|
||||
const isWindows = navigator.platform.toLowerCase().includes('win') ||
|
||||
navigator.userAgent.toLowerCase().includes('windows');
|
||||
```
|
||||
|
||||
**After**:
|
||||
```javascript
|
||||
const isWindows = navigator.userAgentData?.platform === 'Windows' ||
|
||||
navigator.userAgent.toLowerCase().includes('windows');
|
||||
```
|
||||
|
||||
**Impact**: `navigator.platform` is deprecated and will be removed from browsers. The new code uses the modern `navigator.userAgentData` API with a fallback.
|
||||
|
||||
---
|
||||
|
||||
### ✅ 2. Removed Dead Code
|
||||
**File**: `src/pages/index.astro` (Lines 297-309)
|
||||
|
||||
**Removed**:
|
||||
- `installCmds` object (unused, duplicated in `copyCommands`)
|
||||
- `osCmds` object (unused)
|
||||
|
||||
**Impact**: Reduced bundle size by ~150 bytes and improved code clarity.
|
||||
|
||||
---
|
||||
|
||||
### ✅ 3. Cached DOM Queries for Performance
|
||||
**File**: `src/pages/index.astro` (Lines 349-353)
|
||||
|
||||
**Added**:
|
||||
```javascript
|
||||
// Cached query selectors for frequently updated elements
|
||||
const pmCmdElements = document.querySelectorAll('.pm-cmd');
|
||||
const pmInstallElements = document.querySelectorAll('.pm-install');
|
||||
const osCmdElements = document.querySelectorAll('.os-cmd');
|
||||
const osCmdHackableElements = document.querySelectorAll('.os-cmd-hackable');
|
||||
```
|
||||
|
||||
**Before** (in `updateCommands` function):
|
||||
```javascript
|
||||
document.querySelectorAll('.pm-cmd').forEach(...); // Called on every update
|
||||
document.querySelectorAll('.pm-install').forEach(...);
|
||||
document.querySelectorAll('.os-cmd').forEach(...);
|
||||
document.querySelectorAll('.os-cmd-hackable').forEach(...);
|
||||
```
|
||||
|
||||
**After**:
|
||||
```javascript
|
||||
pmCmdElements.forEach(...); // Uses cached reference
|
||||
pmInstallElements.forEach(...);
|
||||
osCmdElements.forEach(...);
|
||||
osCmdHackableElements.forEach(...);
|
||||
```
|
||||
|
||||
**Impact**: Eliminated 4 repeated DOM queries per state update. Improved performance, especially on slower devices.
|
||||
|
||||
---
|
||||
|
||||
### ✅ 4. Added Null-Safe Operations
|
||||
**Files**: `src/pages/index.astro`
|
||||
|
||||
**Changed**: All DOM operations now include null checks using `if` statements:
|
||||
|
||||
```javascript
|
||||
// Before
|
||||
osDetected.textContent = osLabels[currentOs];
|
||||
|
||||
// After
|
||||
if (osDetected) osDetected.textContent = osLabels[currentOs];
|
||||
```
|
||||
|
||||
**Affected functions**:
|
||||
- `updateCommands()` - 8 null checks added
|
||||
- `updateVisibility()` - 11 null checks added
|
||||
- Event listeners - 2 null checks added
|
||||
- Easter egg animation - 1 null check added
|
||||
|
||||
**Impact**: Prevents runtime crashes if HTML elements are missing or renamed. More resilient code.
|
||||
|
||||
---
|
||||
|
||||
### ✅ 5. Improved Clipboard Copy Handler
|
||||
**File**: `src/pages/index.astro` (Lines 531-578)
|
||||
|
||||
**Improvements**:
|
||||
1. **Added fallback to `execCommand`** for older browsers or non-HTTPS contexts
|
||||
2. **Added visual error feedback** - red flash on copy failure
|
||||
3. **Added null checks** for icon elements
|
||||
4. **Added validation** for command key existence
|
||||
|
||||
**Before**:
|
||||
```javascript
|
||||
try {
|
||||
await navigator.clipboard.writeText(code);
|
||||
// ... success handling
|
||||
} catch (err) {
|
||||
console.error('Failed to copy:', err); // Silent failure
|
||||
}
|
||||
```
|
||||
|
||||
**After**:
|
||||
```javascript
|
||||
let success = false;
|
||||
try {
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
await navigator.clipboard.writeText(code);
|
||||
success = true;
|
||||
} else {
|
||||
// Fallback using textarea + execCommand
|
||||
const textArea = document.createElement('textarea');
|
||||
textArea.value = code;
|
||||
textArea.style.position = 'fixed';
|
||||
textArea.style.opacity = '0';
|
||||
document.body.appendChild(textArea);
|
||||
textArea.select();
|
||||
success = document.execCommand('copy');
|
||||
document.body.removeChild(textArea);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to copy:', err);
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (success) {
|
||||
// Success feedback (green checkmark)
|
||||
} else {
|
||||
// Visual error feedback - brief red flash
|
||||
btn.style.background = 'rgba(239, 68, 68, 0.3)';
|
||||
setTimeout(() => {
|
||||
btn.style.background = '';
|
||||
}, 1000);
|
||||
}
|
||||
```
|
||||
|
||||
**Impact**: Copy works in more environments, users get visual feedback on failure.
|
||||
|
||||
---
|
||||
|
||||
### ✅ 6. Updated Font Loading Comment
|
||||
**File**: `src/layouts/Layout.astro` (Line 40)
|
||||
|
||||
**Changed**: Updated comment to clarify that `display=swap` is already implemented for performance.
|
||||
|
||||
**Note**: The `&display=swap` parameter was already present in the URL. No functional change, just documentation improvement.
|
||||
|
||||
---
|
||||
|
||||
### ✅ 7. Fixed Easter Egg Null Safety
|
||||
**File**: `src/pages/index.astro` (Lines 582-615)
|
||||
|
||||
**Before**:
|
||||
```javascript
|
||||
const lobsterIcon = document.querySelector('.lobster-icon');
|
||||
const tagline = document.getElementById('tagline');
|
||||
const originalTagline = tagline.textContent; // Could crash if null
|
||||
|
||||
lobsterIcon.addEventListener('mouseenter', () => { ... }); // Could crash if null
|
||||
```
|
||||
|
||||
**After**:
|
||||
```javascript
|
||||
const lobsterIcon = document.querySelector('.lobster-icon');
|
||||
const tagline = document.getElementById('tagline');
|
||||
|
||||
if (lobsterIcon && tagline) {
|
||||
const originalTagline = tagline.textContent;
|
||||
lobsterIcon.addEventListener('mouseenter', () => { ... });
|
||||
}
|
||||
```
|
||||
|
||||
**Impact**: Easter egg won't crash if elements are missing.
|
||||
|
||||
---
|
||||
|
||||
## Summary Statistics
|
||||
|
||||
| Metric | Before | After | Improvement |
|
||||
|--------|--------|-------|-------------|
|
||||
| Dead code lines | 13 | 0 | -13 lines |
|
||||
| DOM queries per update | 4 | 0 | 100% cached |
|
||||
| Null checks | 0 | ~23 | +∞ |
|
||||
| Clipboard fallback | ❌ | ✅ | Works in more contexts |
|
||||
| Deprecated APIs | 1 | 0 | Future-proof |
|
||||
| Visual error feedback | ❌ | ✅ | Better UX |
|
||||
|
||||
---
|
||||
|
||||
## Remaining Recommendations
|
||||
|
||||
### Lock File Cleanup (Not Implemented)
|
||||
The project currently has **3 lock files**:
|
||||
- `bun.lock` (86KB)
|
||||
- `package-lock.json` (188KB)
|
||||
- `pnpm-lock.yaml` (107KB)
|
||||
|
||||
**Recommendation**: Choose one package manager and remove the other lock files. Based on the README using `bun install`, keep `bun.lock` and delete/gitignore the others.
|
||||
|
||||
**Why**: Different contributors using different package managers can lead to dependency version mismatches.
|
||||
|
||||
---
|
||||
|
||||
## Files Modified
|
||||
|
||||
1. ✅ `src/pages/index.astro` - Main JavaScript improvements
|
||||
2. ✅ `src/layouts/Layout.astro` - Font loading comment
|
||||
|
||||
## Testing Recommendations
|
||||
|
||||
1. Test clipboard copy on different browsers (Chrome, Firefox, Safari)
|
||||
2. Test clipboard copy in HTTP vs HTTPS contexts
|
||||
3. Test with browser DevTools - delete elements and verify no console errors
|
||||
4. Test OS detection on Windows, macOS, Linux
|
||||
5. Test mode switching (One-liner, npm, Hackable, macOS)
|
||||
6. Test beta toggle functionality
|
||||
7. Hover over lobster icon to test Easter egg
|
||||
|
||||
---
|
||||
|
||||
**All critical technical flaws have been addressed.** The code is now more robust, performant, and future-proof.
|
||||
14
README.md
14
README.md
@ -44,18 +44,28 @@ The landing page hosts installer scripts:
|
||||
|
||||
- **macOS/Linux**: `curl -fsSL --proto '=https' --tlsv1.2 https://openclaw.ai/install.sh | bash`
|
||||
- **macOS/Linux (CLI only, no onboarding)**: `curl -fsSL --proto '=https' --tlsv1.2 https://openclaw.ai/install-cli.sh | bash`
|
||||
- **Windows**: `iwr -useb https://openclaw.ai/install.ps1 | iex`
|
||||
- **Windows**: `powershell -c "irm https://openclaw.ai/install.ps1 | iex"`
|
||||
|
||||
Installer UI controls (macOS/Linux `install.sh`):
|
||||
- Gum UI is auto-detected; interactive terminals get richer status output, non-interactive shells fall back to plain output automatically.
|
||||
- Windows `install.ps1` keeps `irm ... | iex` failures in the current PowerShell session while preserving non-zero exits for direct script-file automation.
|
||||
|
||||
These scripts:
|
||||
1. Install Homebrew (macOS) or detect package managers (Windows)
|
||||
2. Install Node.js 22+ if needed
|
||||
3. Install openclaw globally via npm
|
||||
3. Install openclaw globally via npm, or from a pnpm-backed git checkout with `--install-method git`
|
||||
4. Run `openclaw doctor --non-interactive` for migrations (upgrades only)
|
||||
5. Prompt to run `openclaw onboard` (new installs)
|
||||
|
||||
Switching after install:
|
||||
- npm package to git checkout: `openclaw update --channel dev`
|
||||
- git checkout to npm package: `openclaw update --channel stable`
|
||||
- installer-forced switch: rerun the installer with `--install-method git` or `--install-method npm`
|
||||
|
||||
Source checkouts use the OpenClaw pnpm workspace. Keep hackable/dev-channel copy
|
||||
pointing at `pnpm install`; root `npm install` is for packaged installs, not
|
||||
source trees.
|
||||
|
||||
Troubleshooting:
|
||||
- macOS first-run Homebrew bootstrap needs an Administrator account. If install fails with a sudo/admin error, use an admin account (or add the current user to the `admin` group) and rerun the installer.
|
||||
|
||||
|
||||
278
bun.lock
278
bun.lock
@ -3,14 +3,14 @@
|
||||
"configVersion": 0,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "clawd-bot-landing",
|
||||
"name": "openclaw-ai",
|
||||
"dependencies": {
|
||||
"@astrojs/rss": "^4.0.15",
|
||||
"@lucide/astro": "^0.577.0",
|
||||
"@vercel/analytics": "^1.6.1",
|
||||
"astro": "^5.17.2",
|
||||
"@astrojs/rss": "^4.0.18",
|
||||
"@lucide/astro": "^1.11.0",
|
||||
"@vercel/analytics": "^2.0.1",
|
||||
"astro": "^6.1.9",
|
||||
"js-yaml": "^4.1.1",
|
||||
"simple-icons": "^16.8.0",
|
||||
"simple-icons": "^16.18.0",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
@ -18,28 +18,32 @@
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@astrojs/compiler": ["@astrojs/compiler@2.13.0", "", {}, "sha512-mqVORhUJViA28fwHYaWmsXSzLO9osbdZ5ImUfxBarqsYdMlPbqAqGJCxsNzvppp1BEzc1mJNjOVvQqeDN8Vspw=="],
|
||||
"@astrojs/compiler": ["@astrojs/compiler@3.0.1", "", {}, "sha512-z97oYbdebO5aoWzuJ/8q5hLK232+17KcLZ7cJ8BCWk6+qNzVxn/gftC0KzMBUTD8WAaBkPpNSQK6PXLnNrZ0CA=="],
|
||||
|
||||
"@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.7.5", "", {}, "sha512-vreGnYSSKhAjFJCWAwe/CNhONvoc5lokxtRoZims+0wa3KbHBdPHSSthJsKxPd8d/aic6lWKpRTYGY/hsgK6EA=="],
|
||||
"@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.9.0", "", { "dependencies": { "picomatch": "^4.0.4" } }, "sha512-GdYkzR26re8izmyYlBqf4z2s7zNngmWLFuxw0UKiPNqHraZGS6GKWIwSHgS22RDlu2ePFJ8bzmpBcUszut/SDg=="],
|
||||
|
||||
"@astrojs/markdown-remark": ["@astrojs/markdown-remark@6.3.10", "", { "dependencies": { "@astrojs/internal-helpers": "0.7.5", "@astrojs/prism": "3.3.0", "github-slugger": "^2.0.0", "hast-util-from-html": "^2.0.3", "hast-util-to-text": "^4.0.2", "import-meta-resolve": "^4.2.0", "js-yaml": "^4.1.1", "mdast-util-definitions": "^6.0.0", "rehype-raw": "^7.0.0", "rehype-stringify": "^10.0.1", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remark-smartypants": "^3.0.2", "shiki": "^3.19.0", "smol-toml": "^1.5.2", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.0.0", "unist-util-visit-parents": "^6.0.2", "vfile": "^6.0.3" } }, "sha512-kk4HeYR6AcnzC4QV8iSlOfh+N8TZ3MEStxPyenyCtemqn8IpEATBFMTJcfrNW32dgpt6MY3oCkMM/Tv3/I4G3A=="],
|
||||
"@astrojs/markdown-remark": ["@astrojs/markdown-remark@7.1.1", "", { "dependencies": { "@astrojs/internal-helpers": "0.9.0", "@astrojs/prism": "4.0.1", "github-slugger": "^2.0.0", "hast-util-from-html": "^2.0.3", "hast-util-to-text": "^4.0.2", "js-yaml": "^4.1.1", "mdast-util-definitions": "^6.0.0", "rehype-raw": "^7.0.0", "rehype-stringify": "^10.0.1", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remark-smartypants": "^3.0.2", "retext-smartypants": "^6.2.0", "shiki": "^4.0.0", "smol-toml": "^1.6.0", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.1.0", "unist-util-visit-parents": "^6.0.2", "vfile": "^6.0.3" } }, "sha512-C6e9BnLGlbdv6bV8MYGeHpHxsUHrCrB4OuRLqi5LI7oiBVcBcqfUN06zpwFQdHgV48QCCrMmLpyqBr7VqC+swA=="],
|
||||
|
||||
"@astrojs/prism": ["@astrojs/prism@3.3.0", "", { "dependencies": { "prismjs": "^1.30.0" } }, "sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ=="],
|
||||
"@astrojs/prism": ["@astrojs/prism@4.0.1", "", { "dependencies": { "prismjs": "^1.30.0" } }, "sha512-nksZQVjlferuWzhPsBpQ1JE5XuKAf1id1/9Hj4a9KG4+ofrlzxUUwX4YGQF/SuDiuiGKEnzopGOt38F3AnVWsQ=="],
|
||||
|
||||
"@astrojs/rss": ["@astrojs/rss@4.0.15", "", { "dependencies": { "fast-xml-parser": "^5.3.3", "piccolore": "^0.1.3" } }, "sha512-uXO/k6AhRkIDXmRoc6xQpoPZrimQNUmS43X4+60yunfuMNHtSRN5e/FiSi7NApcZqmugSMc5+cJi8ovqgO+qIg=="],
|
||||
"@astrojs/rss": ["@astrojs/rss@4.0.18", "", { "dependencies": { "fast-xml-parser": "^5.5.7", "piccolore": "^0.1.3", "zod": "^4.3.6" } }, "sha512-wc5DwKlbTEdgVAWnHy8krFTeQ42t1v/DJqeq5HtulYK3FYHE4krtRGjoyhS3eXXgfdV6Raoz2RU3wrMTFAitRg=="],
|
||||
|
||||
"@astrojs/telemetry": ["@astrojs/telemetry@3.3.0", "", { "dependencies": { "ci-info": "^4.2.0", "debug": "^4.4.0", "dlv": "^1.1.3", "dset": "^3.1.4", "is-docker": "^3.0.0", "is-wsl": "^3.1.0", "which-pm-runs": "^1.1.0" } }, "sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ=="],
|
||||
"@astrojs/telemetry": ["@astrojs/telemetry@3.3.1", "", { "dependencies": { "ci-info": "^4.4.0", "dlv": "^1.1.3", "dset": "^3.1.4", "is-docker": "^4.0.0", "is-wsl": "^3.1.1", "which-pm-runs": "^1.1.0" } }, "sha512-7fcIxXS9J4ls5tr8b3ww9rbAIz2+HrhNJYZdkAhhB4za/I5IZ/60g+Bs8q7zwG0tOIZfNB4JWhVJ1Qkl/OrNCw=="],
|
||||
|
||||
"@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="],
|
||||
|
||||
"@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="],
|
||||
|
||||
"@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
|
||||
"@babel/parser": ["@babel/parser@7.29.2", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA=="],
|
||||
|
||||
"@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
|
||||
"@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="],
|
||||
|
||||
"@capsizecss/unpack": ["@capsizecss/unpack@4.0.0", "", { "dependencies": { "fontkitten": "^1.0.0" } }, "sha512-VERIM64vtTP1C4mxQ5thVT9fK0apjPFobqybMtA1UdUujWka24ERHbRHFGmpbbhp73MhV+KSsHQH9C6uOTdEQA=="],
|
||||
|
||||
"@clack/core": ["@clack/core@1.2.0", "", { "dependencies": { "fast-wrap-ansi": "^0.1.3", "sisteransi": "^1.0.5" } }, "sha512-qfxof/3T3t9DPU/Rj3OmcFyZInceqj/NVtO9rwIuJqCUgh32gwPjpFQQp/ben07qKlhpwq7GzfWpST4qdJ5Drg=="],
|
||||
|
||||
"@clack/prompts": ["@clack/prompts@1.2.0", "", { "dependencies": { "@clack/core": "1.2.0", "fast-string-width": "^1.1.0", "fast-wrap-ansi": "^0.1.3", "sisteransi": "^1.0.5" } }, "sha512-4jmztR9fMqPMjz6H/UZXj0zEmE43ha1euENwkckKKel4XpSfokExPo5AiVStdHSAlHekz4d0CA/r45Ok1E4D3w=="],
|
||||
|
||||
"@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="],
|
||||
|
||||
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.3", "", { "os": "aix", "cpu": "ppc64" }, "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg=="],
|
||||
@ -146,7 +150,9 @@
|
||||
|
||||
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
|
||||
|
||||
"@lucide/astro": ["@lucide/astro@0.577.0", "", { "peerDependencies": { "astro": "^4 || ^5" } }, "sha512-xZcdx+MyYXY74cBzFxr9N49SlofbGjCVD9GxJZcJA40PJvJSLWSeJpD9bgKFcZfFxf1QXkEYaR6BnCS+0lek8Q=="],
|
||||
"@lucide/astro": ["@lucide/astro@1.11.0", "", { "peerDependencies": { "astro": "^4 || ^5 || ^6" } }, "sha512-b9AA35k3L/DR9J9OoGWIHks4dtMzcDo9P0sZEEDmFxIy0qqw+rKMt3TLk+YEoXppcqXMK/DTM1tIfuaH+frX/w=="],
|
||||
|
||||
"@nodable/entities": ["@nodable/entities@2.1.0", "", {}, "sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA=="],
|
||||
|
||||
"@oslojs/encoding": ["@oslojs/encoding@1.1.0", "", {}, "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ=="],
|
||||
|
||||
@ -202,17 +208,19 @@
|
||||
|
||||
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.55.1", "", { "os": "win32", "cpu": "x64" }, "sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw=="],
|
||||
|
||||
"@shikijs/core": ["@shikijs/core@3.22.0", "", { "dependencies": { "@shikijs/types": "3.22.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-iAlTtSDDbJiRpvgL5ugKEATDtHdUVkqgHDm/gbD2ZS9c88mx7G1zSYjjOxp5Qa0eaW0MAQosFRmJSk354PRoQA=="],
|
||||
"@shikijs/core": ["@shikijs/core@4.0.2", "", { "dependencies": { "@shikijs/primitive": "4.0.2", "@shikijs/types": "4.0.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-hxT0YF4ExEqB8G/qFdtJvpmHXBYJ2lWW7qTHDarVkIudPFE6iCIrqdgWxGn5s+ppkGXI0aEGlibI0PAyzP3zlw=="],
|
||||
|
||||
"@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.22.0", "", { "dependencies": { "@shikijs/types": "3.22.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-jdKhfgW9CRtj3Tor0L7+yPwdG3CgP7W+ZEqSsojrMzCjD1e0IxIbwUMDDpYlVBlC08TACg4puwFGkZfLS+56Tw=="],
|
||||
"@shikijs/engine-javascript": ["@shikijs/engine-javascript@4.0.2", "", { "dependencies": { "@shikijs/types": "4.0.2", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-7PW0Nm49DcoUIQEXlJhNNBHyoGMjalRETTCcjMqEaMoJRLljy1Bi/EGV3/qLBgLKQejdspiiYuHGQW6dX94Nag=="],
|
||||
|
||||
"@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.22.0", "", { "dependencies": { "@shikijs/types": "3.22.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-DyXsOG0vGtNtl7ygvabHd7Mt5EY8gCNqR9Y7Lpbbd/PbJvgWrqaKzH1JW6H6qFkuUa8aCxoiYVv8/YfFljiQxA=="],
|
||||
"@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@4.0.2", "", { "dependencies": { "@shikijs/types": "4.0.2", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-UpCB9Y2sUKlS9z8juFSKz7ZtysmeXCgnRF0dlhXBkmQnek7lAToPte8DkxmEYGNTMii72zU/lyXiCB6StuZeJg=="],
|
||||
|
||||
"@shikijs/langs": ["@shikijs/langs@3.22.0", "", { "dependencies": { "@shikijs/types": "3.22.0" } }, "sha512-x/42TfhWmp6H00T6uwVrdTJGKgNdFbrEdhaDwSR5fd5zhQ1Q46bHq9EO61SCEWJR0HY7z2HNDMaBZp8JRmKiIA=="],
|
||||
"@shikijs/langs": ["@shikijs/langs@4.0.2", "", { "dependencies": { "@shikijs/types": "4.0.2" } }, "sha512-KaXby5dvoeuZzN0rYQiPMjFoUrz4hgwIE+D6Du9owcHcl6/g16/yT5BQxSW5cGt2MZBz6Hl0YuRqf12omRfUUg=="],
|
||||
|
||||
"@shikijs/themes": ["@shikijs/themes@3.22.0", "", { "dependencies": { "@shikijs/types": "3.22.0" } }, "sha512-o+tlOKqsr6FE4+mYJG08tfCFDS+3CG20HbldXeVoyP+cYSUxDhrFf3GPjE60U55iOkkjbpY2uC3It/eeja35/g=="],
|
||||
"@shikijs/primitive": ["@shikijs/primitive@4.0.2", "", { "dependencies": { "@shikijs/types": "4.0.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-M6UMPrSa3fN5ayeJwFVl9qWofl273wtK1VG8ySDZ1mQBfhCpdd8nEx7nPZ/tk7k+TYcpqBZzj/AnwxT9lO+HJw=="],
|
||||
|
||||
"@shikijs/types": ["@shikijs/types@3.22.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-491iAekgKDBFE67z70Ok5a8KBMsQ2IJwOWw3us/7ffQkIBCyOQfm/aNwVMBUriP02QshIfgHCBSIYAl3u2eWjg=="],
|
||||
"@shikijs/themes": ["@shikijs/themes@4.0.2", "", { "dependencies": { "@shikijs/types": "4.0.2" } }, "sha512-mjCafwt8lJJaVSsQvNVrJumbnnj1RI8jbUKrPKgE6E3OvQKxnuRoBaYC51H4IGHePsGN/QtALglWBU7DoKDFnA=="],
|
||||
|
||||
"@shikijs/types": ["@shikijs/types@4.0.2", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-qzbeRooUTPnLE+sHD/Z8DStmaDgnbbc/pMrU203950aRqjX/6AFHeDYT+j00y2lPdz0ywJKx7o/7qnqTivtlXg=="],
|
||||
|
||||
"@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="],
|
||||
|
||||
@ -230,21 +238,11 @@
|
||||
|
||||
"@types/nlcst": ["@types/nlcst@2.0.3", "", { "dependencies": { "@types/unist": "*" } }, "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA=="],
|
||||
|
||||
"@types/node": ["@types/node@25.0.3", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA=="],
|
||||
|
||||
"@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
|
||||
|
||||
"@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="],
|
||||
|
||||
"@vercel/analytics": ["@vercel/analytics@1.6.1", "", { "peerDependencies": { "@remix-run/react": "^2", "@sveltejs/kit": "^1 || ^2", "next": ">= 13", "react": "^18 || ^19 || ^19.0.0-rc", "svelte": ">= 4", "vue": "^3", "vue-router": "^4" }, "optionalPeers": ["@remix-run/react", "@sveltejs/kit", "next", "react", "svelte", "vue", "vue-router"] }, "sha512-oH9He/bEM+6oKlv3chWuOOcp8Y6fo6/PSro8hEkgCW3pu9/OiCXiUpRUogDh3Fs3LH2sosDrx8CxeOLBEE+afg=="],
|
||||
|
||||
"acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
|
||||
|
||||
"ansi-align": ["ansi-align@3.0.1", "", { "dependencies": { "string-width": "^4.1.0" } }, "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w=="],
|
||||
|
||||
"ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
|
||||
|
||||
"ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
|
||||
"@vercel/analytics": ["@vercel/analytics@2.0.1", "", { "peerDependencies": { "@remix-run/react": "^2", "@sveltejs/kit": "^1 || ^2", "next": ">= 13", "nuxt": ">= 3", "react": "^18 || ^19 || ^19.0.0-rc", "svelte": ">= 4", "vue": "^3", "vue-router": "^4" }, "optionalPeers": ["@remix-run/react", "@sveltejs/kit", "next", "nuxt", "react", "svelte", "vue", "vue-router"] }, "sha512-MTQG6V9qQrt1tsDeF+2Uoo5aPjqbVPys1xvnIftXSJYG2SrwXRHnqEvVoYID7BTruDz4lCd2Z7rM1BdkUehk2g=="],
|
||||
|
||||
"anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="],
|
||||
|
||||
@ -254,24 +252,16 @@
|
||||
|
||||
"array-iterate": ["array-iterate@2.0.1", "", {}, "sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg=="],
|
||||
|
||||
"astro": ["astro@5.17.2", "", { "dependencies": { "@astrojs/compiler": "^2.13.0", "@astrojs/internal-helpers": "0.7.5", "@astrojs/markdown-remark": "6.3.10", "@astrojs/telemetry": "3.3.0", "@capsizecss/unpack": "^4.0.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.3.0", "acorn": "^8.15.0", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "boxen": "8.0.1", "ci-info": "^4.3.1", "clsx": "^2.1.1", "common-ancestor-path": "^1.0.1", "cookie": "^1.1.1", "cssesc": "^3.0.0", "debug": "^4.4.3", "deterministic-object-hash": "^2.0.2", "devalue": "^5.6.2", "diff": "^8.0.3", "dlv": "^1.1.3", "dset": "^3.1.4", "es-module-lexer": "^1.7.0", "esbuild": "^0.27.0", "estree-walker": "^3.0.3", "flattie": "^1.1.1", "fontace": "~0.4.0", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.2.0", "import-meta-resolve": "^4.2.0", "js-yaml": "^4.1.1", "magic-string": "^0.30.21", "magicast": "^0.5.1", "mrmime": "^2.0.1", "neotraverse": "^0.6.18", "p-limit": "^6.2.0", "p-queue": "^8.1.1", "package-manager-detector": "^1.6.0", "piccolore": "^0.1.3", "picomatch": "^4.0.3", "prompts": "^2.4.2", "rehype": "^13.0.2", "semver": "^7.7.3", "shiki": "^3.21.0", "smol-toml": "^1.6.0", "svgo": "^4.0.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tsconfck": "^3.1.6", "ultrahtml": "^1.6.0", "unifont": "~0.7.3", "unist-util-visit": "^5.0.0", "unstorage": "^1.17.4", "vfile": "^6.0.3", "vite": "^6.4.1", "vitefu": "^1.1.1", "xxhash-wasm": "^1.1.0", "yargs-parser": "^21.1.1", "yocto-spinner": "^0.2.3", "zod": "^3.25.76", "zod-to-json-schema": "^3.25.1", "zod-to-ts": "^1.2.0" }, "optionalDependencies": { "sharp": "^0.34.0" }, "bin": { "astro": "astro.js" } }, "sha512-7jnMqGo53hOQNwo1N/wqeOvUp8wwW/p+DeerSjSkHNx8L/1mhy6P7rVo7EhdmF8DpKqw0tl/B5Fx1WcIzg1ysA=="],
|
||||
"astro": ["astro@6.1.9", "", { "dependencies": { "@astrojs/compiler": "^3.0.1", "@astrojs/internal-helpers": "0.9.0", "@astrojs/markdown-remark": "7.1.1", "@astrojs/telemetry": "3.3.1", "@capsizecss/unpack": "^4.0.0", "@clack/prompts": "^1.1.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.3.0", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "ci-info": "^4.4.0", "clsx": "^2.1.1", "common-ancestor-path": "^2.0.0", "cookie": "^1.1.1", "devalue": "^5.6.3", "diff": "^8.0.3", "dset": "^3.1.4", "es-module-lexer": "^2.0.0", "esbuild": "^0.27.3", "flattie": "^1.1.1", "fontace": "~0.4.1", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.2.0", "js-yaml": "^4.1.1", "magic-string": "^0.30.21", "magicast": "^0.5.2", "mrmime": "^2.0.1", "neotraverse": "^0.6.18", "obug": "^2.1.1", "p-limit": "^7.3.0", "p-queue": "^9.1.0", "package-manager-detector": "^1.6.0", "piccolore": "^0.1.3", "picomatch": "^4.0.4", "rehype": "^13.0.2", "semver": "^7.7.4", "shiki": "^4.0.2", "smol-toml": "^1.6.0", "svgo": "^4.0.1", "tinyclip": "^0.1.12", "tinyexec": "^1.0.4", "tinyglobby": "^0.2.15", "tsconfck": "^3.1.6", "ultrahtml": "^1.6.0", "unifont": "~0.7.4", "unist-util-visit": "^5.1.0", "unstorage": "^1.17.5", "vfile": "^6.0.3", "vite": "^7.3.2", "vitefu": "^1.1.2", "xxhash-wasm": "^1.1.0", "yargs-parser": "^22.0.0", "zod": "^4.3.6" }, "optionalDependencies": { "sharp": "^0.34.0" }, "bin": { "astro": "bin/astro.mjs" } }, "sha512-NsAHzMzpznB281g2aM5qnBt2QjfH6ttKiZ3hSZw52If8JJ+62kbnBKbyKhR2glQcJLl7Jfe4GSl0DihFZ36rRQ=="],
|
||||
|
||||
"axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
|
||||
|
||||
"bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="],
|
||||
|
||||
"base-64": ["base-64@1.0.0", "", {}, "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg=="],
|
||||
|
||||
"boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="],
|
||||
|
||||
"boxen": ["boxen@8.0.1", "", { "dependencies": { "ansi-align": "^3.0.1", "camelcase": "^8.0.0", "chalk": "^5.3.0", "cli-boxes": "^3.0.0", "string-width": "^7.2.0", "type-fest": "^4.21.0", "widest-line": "^5.0.0", "wrap-ansi": "^9.0.0" } }, "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw=="],
|
||||
|
||||
"camelcase": ["camelcase@8.0.0", "", {}, "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA=="],
|
||||
|
||||
"ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="],
|
||||
|
||||
"chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
|
||||
|
||||
"character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="],
|
||||
|
||||
"character-entities-html4": ["character-entities-html4@2.1.0", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="],
|
||||
@ -280,9 +270,7 @@
|
||||
|
||||
"chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="],
|
||||
|
||||
"ci-info": ["ci-info@4.3.1", "", {}, "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA=="],
|
||||
|
||||
"cli-boxes": ["cli-boxes@3.0.0", "", {}, "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g=="],
|
||||
"ci-info": ["ci-info@4.4.0", "", {}, "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg=="],
|
||||
|
||||
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
|
||||
|
||||
@ -290,11 +278,11 @@
|
||||
|
||||
"commander": ["commander@11.1.0", "", {}, "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ=="],
|
||||
|
||||
"common-ancestor-path": ["common-ancestor-path@1.0.1", "", {}, "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w=="],
|
||||
"common-ancestor-path": ["common-ancestor-path@2.0.0", "", {}, "sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng=="],
|
||||
|
||||
"cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="],
|
||||
|
||||
"cookie-es": ["cookie-es@1.2.2", "", {}, "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg=="],
|
||||
"cookie-es": ["cookie-es@1.2.3", "", {}, "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw=="],
|
||||
|
||||
"crossws": ["crossws@0.3.5", "", { "dependencies": { "uncrypto": "^0.1.3" } }, "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA=="],
|
||||
|
||||
@ -304,15 +292,13 @@
|
||||
|
||||
"css-what": ["css-what@6.2.2", "", {}, "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA=="],
|
||||
|
||||
"cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="],
|
||||
|
||||
"csso": ["csso@5.0.5", "", { "dependencies": { "css-tree": "~2.2.0" } }, "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ=="],
|
||||
|
||||
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
||||
|
||||
"decode-named-character-reference": ["decode-named-character-reference@1.2.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q=="],
|
||||
|
||||
"defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="],
|
||||
"defu": ["defu@6.1.7", "", {}, "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ=="],
|
||||
|
||||
"dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
|
||||
|
||||
@ -320,9 +306,7 @@
|
||||
|
||||
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
||||
|
||||
"deterministic-object-hash": ["deterministic-object-hash@2.0.2", "", { "dependencies": { "base-64": "^1.0.0" } }, "sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ=="],
|
||||
|
||||
"devalue": ["devalue@5.6.2", "", {}, "sha512-nPRkjWzzDQlsejL1WVifk5rvcFi/y1onBRxjaFMjZeR9mFpqu2gmAZ9xUB9/IEanEP/vBtGeGganC/GO1fmufg=="],
|
||||
"devalue": ["devalue@5.7.1", "", {}, "sha512-MUbZ586EgQqdRnC4yDrlod3BEdyvE4TapGYHMW2CiaW+KkkFmWEFqBUaLltEZCGi0iFXCEjRF0OjF0DV2QHjOA=="],
|
||||
|
||||
"devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="],
|
||||
|
||||
@ -340,39 +324,43 @@
|
||||
|
||||
"dset": ["dset@3.1.4", "", {}, "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA=="],
|
||||
|
||||
"emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="],
|
||||
|
||||
"entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="],
|
||||
|
||||
"es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="],
|
||||
"es-module-lexer": ["es-module-lexer@2.1.0", "", {}, "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ=="],
|
||||
|
||||
"esbuild": ["esbuild@0.27.3", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.3", "@esbuild/android-arm": "0.27.3", "@esbuild/android-arm64": "0.27.3", "@esbuild/android-x64": "0.27.3", "@esbuild/darwin-arm64": "0.27.3", "@esbuild/darwin-x64": "0.27.3", "@esbuild/freebsd-arm64": "0.27.3", "@esbuild/freebsd-x64": "0.27.3", "@esbuild/linux-arm": "0.27.3", "@esbuild/linux-arm64": "0.27.3", "@esbuild/linux-ia32": "0.27.3", "@esbuild/linux-loong64": "0.27.3", "@esbuild/linux-mips64el": "0.27.3", "@esbuild/linux-ppc64": "0.27.3", "@esbuild/linux-riscv64": "0.27.3", "@esbuild/linux-s390x": "0.27.3", "@esbuild/linux-x64": "0.27.3", "@esbuild/netbsd-arm64": "0.27.3", "@esbuild/netbsd-x64": "0.27.3", "@esbuild/openbsd-arm64": "0.27.3", "@esbuild/openbsd-x64": "0.27.3", "@esbuild/openharmony-arm64": "0.27.3", "@esbuild/sunos-x64": "0.27.3", "@esbuild/win32-arm64": "0.27.3", "@esbuild/win32-ia32": "0.27.3", "@esbuild/win32-x64": "0.27.3" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg=="],
|
||||
|
||||
"escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
|
||||
|
||||
"estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="],
|
||||
"estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
|
||||
|
||||
"eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="],
|
||||
|
||||
"extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="],
|
||||
|
||||
"fast-xml-parser": ["fast-xml-parser@5.3.5", "", { "dependencies": { "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-JeaA2Vm9ffQKp9VjvfzObuMCjUYAp5WDYhRYL5LrBPY/jUDlUtOvDfot0vKSkB9tuX885BDHjtw4fZadD95wnA=="],
|
||||
"fast-string-truncated-width": ["fast-string-truncated-width@1.2.1", "", {}, "sha512-Q9acT/+Uu3GwGj+5w/zsGuQjh9O1TyywhIwAxHudtWrgF09nHOPrvTLhQevPbttcxjr/SNN7mJmfOw/B1bXgow=="],
|
||||
|
||||
"fast-string-width": ["fast-string-width@1.1.0", "", { "dependencies": { "fast-string-truncated-width": "^1.2.0" } }, "sha512-O3fwIVIH5gKB38QNbdg+3760ZmGz0SZMgvwJbA1b2TGXceKE6A2cOlfogh1iw8lr049zPyd7YADHy+B7U4W9bQ=="],
|
||||
|
||||
"fast-wrap-ansi": ["fast-wrap-ansi@0.1.6", "", { "dependencies": { "fast-string-width": "^1.1.0" } }, "sha512-HlUwET7a5gqjURj70D5jl7aC3Zmy4weA1SHUfM0JFI0Ptq987NH2TwbBFLoERhfwk+E+eaq4EK3jXoT+R3yp3w=="],
|
||||
|
||||
"fast-xml-builder": ["fast-xml-builder@1.1.5", "", { "dependencies": { "path-expression-matcher": "^1.1.3" } }, "sha512-4TJn/8FKLeslLAH3dnohXqE3QSoxkhvaMzepOIZytwJXZO69Bfz0HBdDHzOTOon6G59Zrk6VQ2bEiv1t61rfkA=="],
|
||||
|
||||
"fast-xml-parser": ["fast-xml-parser@5.7.2", "", { "dependencies": { "@nodable/entities": "^2.1.0", "fast-xml-builder": "^1.1.5", "path-expression-matcher": "^1.5.0", "strnum": "^2.2.3" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-P7oW7tLbYnhOLQk/Gv7cZgzgMPP/XN03K02/Jy6Y/NHzyIAIpxuZIM/YqAkfiXFPxA2CTm7NtCijK9EDu09u2w=="],
|
||||
|
||||
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
||||
|
||||
"flattie": ["flattie@1.1.1", "", {}, "sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ=="],
|
||||
|
||||
"fontace": ["fontace@0.4.0", "", { "dependencies": { "fontkitten": "^1.0.0" } }, "sha512-moThBCItUe2bjZip5PF/iZClpKHGLwMvR79Kp8XpGRBrvoRSnySN4VcILdv3/MJzbhvUA5WeiUXF5o538m5fvg=="],
|
||||
"fontace": ["fontace@0.4.1", "", { "dependencies": { "fontkitten": "^1.0.2" } }, "sha512-lDMvbAzSnHmbYMTEld5qdtvNH2/pWpICOqpean9IgC7vUbUJc3k+k5Dokp85CegamqQpFbXf0rAVkbzpyTA8aw=="],
|
||||
|
||||
"fontkitten": ["fontkitten@1.0.1", "", { "dependencies": { "tiny-inflate": "^1.0.3" } }, "sha512-m+/cO+/kAU9farlejecXLgQH20+UXyH0K6oosGtogAz7BWco+KTYE60epKwMt8eVxqlOE2Fs+GoHVlGDUbKOoA=="],
|
||||
|
||||
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
||||
|
||||
"get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="],
|
||||
|
||||
"github-slugger": ["github-slugger@2.0.0", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="],
|
||||
|
||||
"h3": ["h3@1.15.5", "", { "dependencies": { "cookie-es": "^1.2.2", "crossws": "^0.3.5", "defu": "^6.1.4", "destr": "^2.0.5", "iron-webcrypto": "^1.2.1", "node-mock-http": "^1.0.4", "radix3": "^1.1.2", "ufo": "^1.6.3", "uncrypto": "^0.1.3" } }, "sha512-xEyq3rSl+dhGX2Lm0+eFQIAzlDN6Fs0EcC4f7BNUmzaRX/PTzeuM+Tr2lHB8FoXggsQIeXLj8EDVgs5ywxyxmg=="],
|
||||
"h3": ["h3@1.15.11", "", { "dependencies": { "cookie-es": "^1.2.3", "crossws": "^0.3.5", "defu": "^6.1.6", "destr": "^2.0.5", "iron-webcrypto": "^1.2.1", "node-mock-http": "^1.0.4", "radix3": "^1.1.2", "ufo": "^1.6.3", "uncrypto": "^0.1.3" } }, "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg=="],
|
||||
|
||||
"hast-util-from-html": ["hast-util-from-html@2.0.3", "", { "dependencies": { "@types/hast": "^3.0.0", "devlop": "^1.1.0", "hast-util-from-parse5": "^8.0.0", "parse5": "^7.0.0", "vfile": "^6.0.0", "vfile-message": "^4.0.0" } }, "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw=="],
|
||||
|
||||
@ -400,31 +388,25 @@
|
||||
|
||||
"http-cache-semantics": ["http-cache-semantics@4.2.0", "", {}, "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ=="],
|
||||
|
||||
"import-meta-resolve": ["import-meta-resolve@4.2.0", "", {}, "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg=="],
|
||||
|
||||
"iron-webcrypto": ["iron-webcrypto@1.2.1", "", {}, "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg=="],
|
||||
|
||||
"is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="],
|
||||
|
||||
"is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
|
||||
"is-docker": ["is-docker@4.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-LHE+wROyG/Y/0ZnbktRCoTix2c1RhgWaZraMZ8o1Q7zCh0VSrICJQO5oqIIISrcSBtrXv0o233w1IYwsWCjTzA=="],
|
||||
|
||||
"is-inside-container": ["is-inside-container@1.0.0", "", { "dependencies": { "is-docker": "^3.0.0" }, "bin": { "is-inside-container": "cli.js" } }, "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA=="],
|
||||
|
||||
"is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="],
|
||||
|
||||
"is-wsl": ["is-wsl@3.1.0", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw=="],
|
||||
"is-wsl": ["is-wsl@3.1.1", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw=="],
|
||||
|
||||
"js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
|
||||
|
||||
"kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="],
|
||||
|
||||
"longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="],
|
||||
|
||||
"lru-cache": ["lru-cache@11.2.5", "", {}, "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw=="],
|
||||
"lru-cache": ["lru-cache@11.3.5", "", {}, "sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw=="],
|
||||
|
||||
"magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
|
||||
|
||||
"magicast": ["magicast@0.5.1", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "source-map-js": "^1.2.1" } }, "sha512-xrHS24IxaLrvuo613F719wvOIv9xPHFWQHuvGUBmPnCA/3MQxKI3b+r7n1jAoDHmsbC5bRhTZYR77invLAxVnw=="],
|
||||
"magicast": ["magicast@0.5.2", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "source-map-js": "^1.2.1" } }, "sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ=="],
|
||||
|
||||
"markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="],
|
||||
|
||||
@ -530,6 +512,8 @@
|
||||
|
||||
"nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="],
|
||||
|
||||
"obug": ["obug@2.1.1", "", {}, "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ=="],
|
||||
|
||||
"ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="],
|
||||
|
||||
"ohash": ["ohash@2.0.11", "", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="],
|
||||
@ -538,11 +522,11 @@
|
||||
|
||||
"oniguruma-to-es": ["oniguruma-to-es@4.3.4", "", { "dependencies": { "oniguruma-parser": "^0.12.1", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } }, "sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA=="],
|
||||
|
||||
"p-limit": ["p-limit@6.2.0", "", { "dependencies": { "yocto-queue": "^1.1.1" } }, "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA=="],
|
||||
"p-limit": ["p-limit@7.3.0", "", { "dependencies": { "yocto-queue": "^1.2.1" } }, "sha512-7cIXg/Z0M5WZRblrsOla88S4wAK+zOQQWeBYfV3qJuJXMr+LnbYjaadrFaS0JILfEDPVqHyKnZ1Z/1d6J9VVUw=="],
|
||||
|
||||
"p-queue": ["p-queue@8.1.1", "", { "dependencies": { "eventemitter3": "^5.0.1", "p-timeout": "^6.1.2" } }, "sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ=="],
|
||||
"p-queue": ["p-queue@9.1.2", "", { "dependencies": { "eventemitter3": "^5.0.1", "p-timeout": "^7.0.0" } }, "sha512-ktsDOALzTYTWWF1PbkNVg2rOt+HaOaMWJMUnt7T3qf5tvZ1L8dBW3tObzprBcXNMKkwj+yFSLqHso0x+UFcJXw=="],
|
||||
|
||||
"p-timeout": ["p-timeout@6.1.4", "", {}, "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg=="],
|
||||
"p-timeout": ["p-timeout@7.0.1", "", {}, "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg=="],
|
||||
|
||||
"package-manager-detector": ["package-manager-detector@1.6.0", "", {}, "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA=="],
|
||||
|
||||
@ -550,18 +534,18 @@
|
||||
|
||||
"parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="],
|
||||
|
||||
"path-expression-matcher": ["path-expression-matcher@1.5.0", "", {}, "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ=="],
|
||||
|
||||
"piccolore": ["piccolore@0.1.3", "", {}, "sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw=="],
|
||||
|
||||
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||
|
||||
"picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
|
||||
"picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="],
|
||||
|
||||
"postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
|
||||
|
||||
"prismjs": ["prismjs@1.30.0", "", {}, "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw=="],
|
||||
|
||||
"prompts": ["prompts@2.4.2", "", { "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" } }, "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q=="],
|
||||
|
||||
"property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="],
|
||||
|
||||
"radix3": ["radix3@1.1.2", "", {}, "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA=="],
|
||||
@ -602,15 +586,15 @@
|
||||
|
||||
"rollup": ["rollup@4.55.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.55.1", "@rollup/rollup-android-arm64": "4.55.1", "@rollup/rollup-darwin-arm64": "4.55.1", "@rollup/rollup-darwin-x64": "4.55.1", "@rollup/rollup-freebsd-arm64": "4.55.1", "@rollup/rollup-freebsd-x64": "4.55.1", "@rollup/rollup-linux-arm-gnueabihf": "4.55.1", "@rollup/rollup-linux-arm-musleabihf": "4.55.1", "@rollup/rollup-linux-arm64-gnu": "4.55.1", "@rollup/rollup-linux-arm64-musl": "4.55.1", "@rollup/rollup-linux-loong64-gnu": "4.55.1", "@rollup/rollup-linux-loong64-musl": "4.55.1", "@rollup/rollup-linux-ppc64-gnu": "4.55.1", "@rollup/rollup-linux-ppc64-musl": "4.55.1", "@rollup/rollup-linux-riscv64-gnu": "4.55.1", "@rollup/rollup-linux-riscv64-musl": "4.55.1", "@rollup/rollup-linux-s390x-gnu": "4.55.1", "@rollup/rollup-linux-x64-gnu": "4.55.1", "@rollup/rollup-linux-x64-musl": "4.55.1", "@rollup/rollup-openbsd-x64": "4.55.1", "@rollup/rollup-openharmony-arm64": "4.55.1", "@rollup/rollup-win32-arm64-msvc": "4.55.1", "@rollup/rollup-win32-ia32-msvc": "4.55.1", "@rollup/rollup-win32-x64-gnu": "4.55.1", "@rollup/rollup-win32-x64-msvc": "4.55.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A=="],
|
||||
|
||||
"sax": ["sax@1.4.3", "", {}, "sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ=="],
|
||||
"sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="],
|
||||
|
||||
"semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
|
||||
"semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
|
||||
|
||||
"sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-darwin-x64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4", "@img/sharp-libvips-darwin-x64": "1.2.4", "@img/sharp-libvips-linux-arm": "1.2.4", "@img/sharp-libvips-linux-arm64": "1.2.4", "@img/sharp-libvips-linux-ppc64": "1.2.4", "@img/sharp-libvips-linux-riscv64": "1.2.4", "@img/sharp-libvips-linux-s390x": "1.2.4", "@img/sharp-libvips-linux-x64": "1.2.4", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4", "@img/sharp-linux-arm": "0.34.5", "@img/sharp-linux-arm64": "0.34.5", "@img/sharp-linux-ppc64": "0.34.5", "@img/sharp-linux-riscv64": "0.34.5", "@img/sharp-linux-s390x": "0.34.5", "@img/sharp-linux-x64": "0.34.5", "@img/sharp-linuxmusl-arm64": "0.34.5", "@img/sharp-linuxmusl-x64": "0.34.5", "@img/sharp-wasm32": "0.34.5", "@img/sharp-win32-arm64": "0.34.5", "@img/sharp-win32-ia32": "0.34.5", "@img/sharp-win32-x64": "0.34.5" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="],
|
||||
|
||||
"shiki": ["shiki@3.22.0", "", { "dependencies": { "@shikijs/core": "3.22.0", "@shikijs/engine-javascript": "3.22.0", "@shikijs/engine-oniguruma": "3.22.0", "@shikijs/langs": "3.22.0", "@shikijs/themes": "3.22.0", "@shikijs/types": "3.22.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-LBnhsoYEe0Eou4e1VgJACes+O6S6QC0w71fCSp5Oya79inkwkm15gQ1UF6VtQ8j/taMDh79hAB49WUk8ALQW3g=="],
|
||||
"shiki": ["shiki@4.0.2", "", { "dependencies": { "@shikijs/core": "4.0.2", "@shikijs/engine-javascript": "4.0.2", "@shikijs/engine-oniguruma": "4.0.2", "@shikijs/langs": "4.0.2", "@shikijs/themes": "4.0.2", "@shikijs/types": "4.0.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-eAVKTMedR5ckPo4xne/PjYQYrU3qx78gtJZ+sHlXEg5IHhhoQhMfZVzetTYuaJS0L2Ef3AcCRzCHV8T0WI6nIQ=="],
|
||||
|
||||
"simple-icons": ["simple-icons@16.8.0", "", {}, "sha512-JOqFl9rXrUkEVwYryZVvfySG6znMg+79KpDIDtAd9mXZAPfLyhVdhhGKg7EYioYzozxXd+5KvURTMguTl0QfbA=="],
|
||||
"simple-icons": ["simple-icons@16.18.0", "", {}, "sha512-5KbjcP456Gm1lrk+rhDuX4zFri+3lRX39IjzXAvoMAO8Ne76WlVlM+Z3kA6jdZ7+QHadgXsf++R7g2jaYVbYig=="],
|
||||
|
||||
"sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="],
|
||||
|
||||
@ -620,19 +604,17 @@
|
||||
|
||||
"space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="],
|
||||
|
||||
"string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
|
||||
|
||||
"stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="],
|
||||
|
||||
"strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
|
||||
"strnum": ["strnum@2.2.3", "", {}, "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg=="],
|
||||
|
||||
"strnum": ["strnum@2.1.2", "", {}, "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ=="],
|
||||
|
||||
"svgo": ["svgo@4.0.0", "", { "dependencies": { "commander": "^11.1.0", "css-select": "^5.1.0", "css-tree": "^3.0.1", "css-what": "^6.1.0", "csso": "^5.0.5", "picocolors": "^1.1.1", "sax": "^1.4.1" }, "bin": "./bin/svgo.js" }, "sha512-VvrHQ+9uniE+Mvx3+C9IEe/lWasXCU0nXMY2kZeLrHNICuRiC8uMPyM14UEaMOFA5mhyQqEkB02VoQ16n3DLaw=="],
|
||||
"svgo": ["svgo@4.0.1", "", { "dependencies": { "commander": "^11.1.0", "css-select": "^5.1.0", "css-tree": "^3.0.1", "css-what": "^6.1.0", "csso": "^5.0.5", "picocolors": "^1.1.1", "sax": "^1.5.0" }, "bin": "./bin/svgo.js" }, "sha512-XDpWUOPC6FEibaLzjfe0ucaV0YrOjYotGJO1WpF0Zd+n6ZGEQUsSugaoLq9QkEZtAfQIxT42UChcssDVPP3+/w=="],
|
||||
|
||||
"tiny-inflate": ["tiny-inflate@1.0.3", "", {}, "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="],
|
||||
|
||||
"tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="],
|
||||
"tinyclip": ["tinyclip@0.1.12", "", {}, "sha512-Ae3OVUqifDw0wBriIBS7yVaW44Dp6eSHQcyq4Igc7eN2TJH/2YsicswaW+J/OuMvhpDPOKEgpAZCjkb4hpoyeA=="],
|
||||
|
||||
"tinyexec": ["tinyexec@1.1.1", "", {}, "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg=="],
|
||||
|
||||
"tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
|
||||
|
||||
@ -644,8 +626,6 @@
|
||||
|
||||
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="],
|
||||
|
||||
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||
|
||||
"ufo": ["ufo@1.6.3", "", {}, "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q=="],
|
||||
@ -654,11 +634,9 @@
|
||||
|
||||
"uncrypto": ["uncrypto@0.1.3", "", {}, "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q=="],
|
||||
|
||||
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
|
||||
|
||||
"unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="],
|
||||
|
||||
"unifont": ["unifont@0.7.3", "", { "dependencies": { "css-tree": "^3.1.0", "ofetch": "^1.5.1", "ohash": "^2.0.11" } }, "sha512-b0GtQzKCyuSHGsfj5vyN8st7muZ6VCI4XD4vFlr7Uy1rlWVYxC3npnfk8MyreHxJYrz1ooLDqDzFe9XqQTlAhA=="],
|
||||
"unifont": ["unifont@0.7.4", "", { "dependencies": { "css-tree": "^3.1.0", "ofetch": "^1.5.1", "ohash": "^2.0.11" } }, "sha512-oHeis4/xl42HUIeHuNZRGEvxj5AaIKR+bHPNegRq5LV1gdc3jundpONbjglKpihmJf+dswygdMJn3eftGIMemg=="],
|
||||
|
||||
"unist-util-find-after": ["unist-util-find-after@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ=="],
|
||||
|
||||
@ -672,13 +650,13 @@
|
||||
|
||||
"unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="],
|
||||
|
||||
"unist-util-visit": ["unist-util-visit@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="],
|
||||
"unist-util-visit": ["unist-util-visit@5.1.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg=="],
|
||||
|
||||
"unist-util-visit-children": ["unist-util-visit-children@3.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA=="],
|
||||
|
||||
"unist-util-visit-parents": ["unist-util-visit-parents@6.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ=="],
|
||||
|
||||
"unstorage": ["unstorage@1.17.4", "", { "dependencies": { "anymatch": "^3.1.3", "chokidar": "^5.0.0", "destr": "^2.0.5", "h3": "^1.15.5", "lru-cache": "^11.2.0", "node-fetch-native": "^1.6.7", "ofetch": "^1.5.1", "ufo": "^1.6.3" }, "peerDependencies": { "@azure/app-configuration": "^1.8.0", "@azure/cosmos": "^4.2.0", "@azure/data-tables": "^13.3.0", "@azure/identity": "^4.6.0", "@azure/keyvault-secrets": "^4.9.0", "@azure/storage-blob": "^12.26.0", "@capacitor/preferences": "^6 || ^7 || ^8", "@deno/kv": ">=0.9.0", "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", "@planetscale/database": "^1.19.0", "@upstash/redis": "^1.34.3", "@vercel/blob": ">=0.27.1", "@vercel/functions": "^2.2.12 || ^3.0.0", "@vercel/kv": "^1 || ^2 || ^3", "aws4fetch": "^1.0.20", "db0": ">=0.2.1", "idb-keyval": "^6.2.1", "ioredis": "^5.4.2", "uploadthing": "^7.4.4" }, "optionalPeers": ["@azure/app-configuration", "@azure/cosmos", "@azure/data-tables", "@azure/identity", "@azure/keyvault-secrets", "@azure/storage-blob", "@capacitor/preferences", "@deno/kv", "@netlify/blobs", "@planetscale/database", "@upstash/redis", "@vercel/blob", "@vercel/functions", "@vercel/kv", "aws4fetch", "db0", "idb-keyval", "ioredis", "uploadthing"] }, "sha512-fHK0yNg38tBiJKp/Vgsq4j0JEsCmgqH58HAn707S7zGkArbZsVr/CwINoi+nh3h98BRCwKvx1K3Xg9u3VV83sw=="],
|
||||
"unstorage": ["unstorage@1.17.5", "", { "dependencies": { "anymatch": "^3.1.3", "chokidar": "^5.0.0", "destr": "^2.0.5", "h3": "^1.15.10", "lru-cache": "^11.2.7", "node-fetch-native": "^1.6.7", "ofetch": "^1.5.1", "ufo": "^1.6.3" }, "peerDependencies": { "@azure/app-configuration": "^1.8.0", "@azure/cosmos": "^4.2.0", "@azure/data-tables": "^13.3.0", "@azure/identity": "^4.6.0", "@azure/keyvault-secrets": "^4.9.0", "@azure/storage-blob": "^12.26.0", "@capacitor/preferences": "^6 || ^7 || ^8", "@deno/kv": ">=0.9.0", "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", "@planetscale/database": "^1.19.0", "@upstash/redis": "^1.34.3", "@vercel/blob": ">=0.27.1", "@vercel/functions": "^2.2.12 || ^3.0.0", "@vercel/kv": "^1 || ^2 || ^3", "aws4fetch": "^1.0.20", "db0": ">=0.2.1", "idb-keyval": "^6.2.1", "ioredis": "^5.4.2", "uploadthing": "^7.4.4" }, "optionalPeers": ["@azure/app-configuration", "@azure/cosmos", "@azure/data-tables", "@azure/identity", "@azure/keyvault-secrets", "@azure/storage-blob", "@capacitor/preferences", "@deno/kv", "@netlify/blobs", "@planetscale/database", "@upstash/redis", "@vercel/blob", "@vercel/functions", "@vercel/kv", "aws4fetch", "db0", "idb-keyval", "ioredis", "uploadthing"] }, "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg=="],
|
||||
|
||||
"vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="],
|
||||
|
||||
@ -686,41 +664,25 @@
|
||||
|
||||
"vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="],
|
||||
|
||||
"vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="],
|
||||
"vite": ["vite@7.3.2", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg=="],
|
||||
|
||||
"vitefu": ["vitefu@1.1.1", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ=="],
|
||||
"vitefu": ["vitefu@1.1.3", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["vite"] }, "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg=="],
|
||||
|
||||
"web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="],
|
||||
|
||||
"which-pm-runs": ["which-pm-runs@1.1.0", "", {}, "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA=="],
|
||||
|
||||
"widest-line": ["widest-line@5.0.0", "", { "dependencies": { "string-width": "^7.0.0" } }, "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA=="],
|
||||
|
||||
"wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="],
|
||||
|
||||
"xxhash-wasm": ["xxhash-wasm@1.1.0", "", {}, "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA=="],
|
||||
|
||||
"yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
|
||||
"yargs-parser": ["yargs-parser@22.0.0", "", {}, "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw=="],
|
||||
|
||||
"yocto-queue": ["yocto-queue@1.2.2", "", {}, "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ=="],
|
||||
|
||||
"yocto-spinner": ["yocto-spinner@0.2.3", "", { "dependencies": { "yoctocolors": "^2.1.1" } }, "sha512-sqBChb33loEnkoXte1bLg45bEBsOP9N1kzQh5JZNKj/0rik4zAPTNSAVPj3uQAdc6slYJ0Ksc403G2XgxsJQFQ=="],
|
||||
|
||||
"yoctocolors": ["yoctocolors@2.1.2", "", {}, "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug=="],
|
||||
|
||||
"zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
||||
|
||||
"zod-to-json-schema": ["zod-to-json-schema@3.25.1", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA=="],
|
||||
|
||||
"zod-to-ts": ["zod-to-ts@1.2.0", "", { "peerDependencies": { "typescript": "^4.9.4 || ^5.0.2", "zod": "^3" } }, "sha512-x30XE43V+InwGpvTySRNz9kB7qFU8DlyEy7BsSTCHPH1R0QasMmHWZDCzYm6bVXtj/9NNJAZF3jW8rzFvH5OFA=="],
|
||||
"zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
|
||||
|
||||
"zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
|
||||
|
||||
"@astrojs/markdown-remark/shiki": ["shiki@3.20.0", "", { "dependencies": { "@shikijs/core": "3.20.0", "@shikijs/engine-javascript": "3.20.0", "@shikijs/engine-oniguruma": "3.20.0", "@shikijs/langs": "3.20.0", "@shikijs/themes": "3.20.0", "@shikijs/types": "3.20.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-kgCOlsnyWb+p0WU+01RjkCH+eBVsjL1jOwUYWv0YDWkM2/A46+LDKVs5yZCUXjJG6bj4ndFoAg5iLIIue6dulg=="],
|
||||
|
||||
"@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
|
||||
|
||||
"ansi-align/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||
"@rollup/pluginutils/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
|
||||
|
||||
"anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||
|
||||
@ -728,80 +690,30 @@
|
||||
|
||||
"dom-serializer/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
|
||||
|
||||
"fontace/fontkitten": ["fontkitten@1.0.3", "", { "dependencies": { "tiny-inflate": "^1.0.3" } }, "sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw=="],
|
||||
|
||||
"hast-util-raw/unist-util-visit": ["unist-util-visit@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="],
|
||||
|
||||
"is-inside-container/is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="],
|
||||
|
||||
"mdast-util-definitions/unist-util-visit": ["unist-util-visit@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="],
|
||||
|
||||
"mdast-util-to-hast/unist-util-visit": ["unist-util-visit@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="],
|
||||
|
||||
"mdast-util-to-markdown/unist-util-visit": ["unist-util-visit@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="],
|
||||
|
||||
"ofetch/ufo": ["ufo@1.6.2", "", {}, "sha512-heMioaxBcG9+Znsda5Q8sQbWnLJSl98AFDXTO80wELWEzX3hordXsTdxrIfMQoO9IY1MEnoGoPjpoKpMj+Yx0Q=="],
|
||||
|
||||
"vite/esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="],
|
||||
"remark-smartypants/unist-util-visit": ["unist-util-visit@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="],
|
||||
|
||||
"@astrojs/markdown-remark/shiki/@shikijs/core": ["@shikijs/core@3.20.0", "", { "dependencies": { "@shikijs/types": "3.20.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-f2ED7HYV4JEk827mtMDwe/yQ25pRiXZmtHjWF8uzZKuKiEsJR7Ce1nuQ+HhV9FzDcbIo4ObBCD9GPTzNuy9S1g=="],
|
||||
"retext-smartypants/unist-util-visit": ["unist-util-visit@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="],
|
||||
|
||||
"@astrojs/markdown-remark/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.20.0", "", { "dependencies": { "@shikijs/types": "3.20.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-OFx8fHAZuk7I42Z9YAdZ95To6jDePQ9Rnfbw9uSRTSbBhYBp1kEOKv/3jOimcj3VRUKusDYM6DswLauwfhboLg=="],
|
||||
"sharp/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
|
||||
|
||||
"@astrojs/markdown-remark/shiki/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.20.0", "", { "dependencies": { "@shikijs/types": "3.20.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-Yx3gy7xLzM0ZOjqoxciHjA7dAt5tyzJE3L4uQoM83agahy+PlW244XJSrmJRSBvGYELDhYXPacD4R/cauV5bzQ=="],
|
||||
"tinyglobby/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
|
||||
|
||||
"@astrojs/markdown-remark/shiki/@shikijs/langs": ["@shikijs/langs@3.20.0", "", { "dependencies": { "@shikijs/types": "3.20.0" } }, "sha512-le+bssCxcSHrygCWuOrYJHvjus6zhQ2K7q/0mgjiffRbkhM4o1EWu2m+29l0yEsHDbWaWPNnDUTRVVBvBBeKaA=="],
|
||||
|
||||
"@astrojs/markdown-remark/shiki/@shikijs/themes": ["@shikijs/themes@3.20.0", "", { "dependencies": { "@shikijs/types": "3.20.0" } }, "sha512-U1NSU7Sl26Q7ErRvJUouArxfM2euWqq1xaSrbqMu2iqa+tSp0D1Yah8216sDYbdDHw4C8b75UpE65eWorm2erQ=="],
|
||||
|
||||
"@astrojs/markdown-remark/shiki/@shikijs/types": ["@shikijs/types@3.20.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-lhYAATn10nkZcBQ0BlzSbJA3wcmL5MXUUF8d2Zzon6saZDlToKaiRX60n2+ZaHJCmXEcZRWNzn+k9vplr8Jhsw=="],
|
||||
|
||||
"ansi-align/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
|
||||
"ansi-align/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||
"unist-util-remove-position/unist-util-visit": ["unist-util-visit@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="],
|
||||
|
||||
"csso/css-tree/mdn-data": ["mdn-data@2.0.28", "", {}, "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g=="],
|
||||
|
||||
"vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="],
|
||||
|
||||
"vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="],
|
||||
|
||||
"vite/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="],
|
||||
|
||||
"vite/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="],
|
||||
|
||||
"vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="],
|
||||
|
||||
"vite/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="],
|
||||
|
||||
"vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="],
|
||||
|
||||
"vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="],
|
||||
|
||||
"vite/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="],
|
||||
|
||||
"vite/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="],
|
||||
|
||||
"vite/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="],
|
||||
|
||||
"vite/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="],
|
||||
|
||||
"vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="],
|
||||
|
||||
"vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="],
|
||||
|
||||
"vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="],
|
||||
|
||||
"vite/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="],
|
||||
|
||||
"vite/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="],
|
||||
|
||||
"vite/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="],
|
||||
|
||||
"vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="],
|
||||
|
||||
"vite/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="],
|
||||
|
||||
"vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="],
|
||||
|
||||
"vite/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="],
|
||||
|
||||
"vite/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="],
|
||||
|
||||
"vite/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="],
|
||||
|
||||
"vite/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="],
|
||||
|
||||
"vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="],
|
||||
|
||||
"ansi-align/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||
}
|
||||
}
|
||||
|
||||
12
package.json
12
package.json
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "clawd-bot-landing",
|
||||
"name": "openclaw-ai",
|
||||
"type": "module",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
@ -14,12 +14,12 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/rss": "^4.0.15",
|
||||
"@lucide/astro": "^0.577.0",
|
||||
"@vercel/analytics": "^1.6.1",
|
||||
"astro": "^5.17.2",
|
||||
"@astrojs/rss": "^4.0.18",
|
||||
"@lucide/astro": "^1.11.0",
|
||||
"@vercel/analytics": "^2.0.1",
|
||||
"astro": "^6.1.9",
|
||||
"js-yaml": "^4.1.1",
|
||||
"simple-icons": "^16.8.0"
|
||||
"simple-icons": "^16.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/js-yaml": "^4.0.9"
|
||||
|
||||
BIN
public/cat.ico
Normal file
BIN
public/cat.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
@ -4,6 +4,31 @@ set -euo pipefail
|
||||
# OpenClaw CLI installer (non-interactive, no onboarding)
|
||||
# Usage: curl -fsSL --proto '=https' --tlsv1.2 https://openclaw.ai/install-cli.sh | bash -s -- [--json] [--prefix <path>] [--version <ver>] [--node-version <ver>] [--onboard]
|
||||
|
||||
ensure_home_env() {
|
||||
if [[ -n "${HOME:-}" && "${HOME}" != "/" && -d "${HOME}" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
local user_name=""
|
||||
local home_dir=""
|
||||
user_name="$(id -un 2>/dev/null || true)"
|
||||
|
||||
if [[ -n "$user_name" ]]; then
|
||||
if command -v getent >/dev/null 2>&1; then
|
||||
home_dir="$(getent passwd "$user_name" 2>/dev/null | awk -F: '{print $6; exit}' || true)"
|
||||
fi
|
||||
if [[ -z "$home_dir" && "$(uname -s 2>/dev/null || true)" == "Darwin" ]] && command -v dscl >/dev/null 2>&1; then
|
||||
home_dir="$(dscl . -read "/Users/${user_name}" NFSHomeDirectory 2>/dev/null | awk '{print $2; exit}' || true)"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "$home_dir" && "$home_dir" != "/" && -d "$home_dir" ]]; then
|
||||
export HOME="$home_dir"
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_home_env
|
||||
|
||||
PREFIX="${OPENCLAW_PREFIX:-${HOME}/.openclaw}"
|
||||
OPENCLAW_VERSION="${OPENCLAW_VERSION:-latest}"
|
||||
NODE_VERSION="${OPENCLAW_NODE_VERSION:-22.22.0}"
|
||||
@ -15,6 +40,7 @@ GIT_UPDATE="${OPENCLAW_GIT_UPDATE:-1}"
|
||||
JSON=0
|
||||
RUN_ONBOARD=0
|
||||
SET_NPM_PREFIX=0
|
||||
PNPM_CMD=()
|
||||
|
||||
print_usage() {
|
||||
cat <<EOF
|
||||
@ -280,6 +306,64 @@ npm_bin() {
|
||||
echo "$(node_dir)/bin/npm"
|
||||
}
|
||||
|
||||
set_pnpm_cmd() {
|
||||
PNPM_CMD=("$@")
|
||||
}
|
||||
|
||||
pnpm_cmd_is_ready() {
|
||||
if [[ ${#PNPM_CMD[@]} -eq 0 ]]; then
|
||||
return 1
|
||||
fi
|
||||
"${PNPM_CMD[@]}" --version >/dev/null 2>&1
|
||||
}
|
||||
|
||||
detect_pnpm_cmd() {
|
||||
if [[ -x "${PREFIX}/bin/pnpm" ]]; then
|
||||
set_pnpm_cmd "${PREFIX}/bin/pnpm"
|
||||
return 0
|
||||
fi
|
||||
if command -v pnpm >/dev/null 2>&1; then
|
||||
set_pnpm_cmd pnpm
|
||||
return 0
|
||||
fi
|
||||
if [[ -x "$(node_dir)/bin/corepack" ]] && "$(node_dir)/bin/corepack" pnpm --version >/dev/null 2>&1; then
|
||||
set_pnpm_cmd "$(node_dir)/bin/corepack" pnpm
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
ensure_pnpm_binary_for_scripts() {
|
||||
if command -v pnpm >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ ${#PNPM_CMD[@]} -eq 2 && "${PNPM_CMD[1]}" == "pnpm" ]] && [[ "$(basename "${PNPM_CMD[0]}")" == "corepack" ]]; then
|
||||
mkdir -p "${PREFIX}/bin"
|
||||
cat > "${PREFIX}/bin/pnpm" <<EOF
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
exec "${PNPM_CMD[0]}" pnpm "\$@"
|
||||
EOF
|
||||
chmod +x "${PREFIX}/bin/pnpm"
|
||||
export PATH="${PREFIX}/bin:${PATH}"
|
||||
hash -r 2>/dev/null || true
|
||||
fi
|
||||
|
||||
if command -v pnpm >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
fail "pnpm command not available on PATH"
|
||||
}
|
||||
|
||||
run_pnpm() {
|
||||
if ! pnpm_cmd_is_ready; then
|
||||
ensure_pnpm
|
||||
fi
|
||||
"${PNPM_CMD[@]}" "$@"
|
||||
}
|
||||
|
||||
install_node() {
|
||||
local os
|
||||
local arch
|
||||
@ -342,9 +426,9 @@ install_node() {
|
||||
}
|
||||
|
||||
ensure_pnpm() {
|
||||
if command -v pnpm >/dev/null 2>&1; then
|
||||
if detect_pnpm_cmd && pnpm_cmd_is_ready; then
|
||||
local current_version
|
||||
current_version="$(pnpm --version 2>/dev/null || true)"
|
||||
current_version="$("${PNPM_CMD[@]}" --version 2>/dev/null || true)"
|
||||
if [[ "$current_version" =~ ^10\. ]]; then
|
||||
return 0
|
||||
fi
|
||||
@ -356,7 +440,7 @@ ensure_pnpm() {
|
||||
log "Installing pnpm via Corepack..."
|
||||
"$(node_dir)/bin/corepack" enable >/dev/null 2>&1 || true
|
||||
"$(node_dir)/bin/corepack" prepare pnpm@10 --activate
|
||||
if command -v pnpm >/dev/null 2>&1 && [[ "$(pnpm --version 2>/dev/null || true)" =~ ^10\. ]]; then
|
||||
if detect_pnpm_cmd && pnpm_cmd_is_ready && [[ "$("${PNPM_CMD[@]}" --version 2>/dev/null || true)" =~ ^10\. ]]; then
|
||||
emit_json "{\"event\":\"step\",\"name\":\"pnpm\",\"status\":\"ok\"}"
|
||||
return 0
|
||||
fi
|
||||
@ -365,6 +449,7 @@ ensure_pnpm() {
|
||||
emit_json "{\"event\":\"step\",\"name\":\"pnpm\",\"status\":\"start\",\"method\":\"npm\"}"
|
||||
log "Installing pnpm via npm..."
|
||||
SHARP_IGNORE_GLOBAL_LIBVIPS="$SHARP_IGNORE_GLOBAL_LIBVIPS" "$(npm_bin)" install -g --prefix "$PREFIX" pnpm@10
|
||||
detect_pnpm_cmd || true
|
||||
emit_json "{\"event\":\"step\",\"name\":\"pnpm\",\"status\":\"ok\"}"
|
||||
return 0
|
||||
}
|
||||
@ -415,26 +500,80 @@ install_openclaw() {
|
||||
fi
|
||||
|
||||
if [[ "${requested}" == "latest" ]]; then
|
||||
if ! SHARP_IGNORE_GLOBAL_LIBVIPS="$SHARP_IGNORE_GLOBAL_LIBVIPS" "$(npm_bin)" install -g --prefix "$PREFIX" "${npm_args[@]}" "openclaw@latest"; then
|
||||
if ! SHARP_IGNORE_GLOBAL_LIBVIPS="$SHARP_IGNORE_GLOBAL_LIBVIPS" "$(npm_bin)" install -g --prefix "$(node_dir)" "${npm_args[@]}" "openclaw@latest"; then
|
||||
log "npm install openclaw@latest failed; retrying openclaw@next"
|
||||
emit_json "{\"event\":\"step\",\"name\":\"openclaw\",\"status\":\"retry\",\"version\":\"next\"}"
|
||||
SHARP_IGNORE_GLOBAL_LIBVIPS="$SHARP_IGNORE_GLOBAL_LIBVIPS" "$(npm_bin)" install -g --prefix "$PREFIX" "${npm_args[@]}" "openclaw@next"
|
||||
SHARP_IGNORE_GLOBAL_LIBVIPS="$SHARP_IGNORE_GLOBAL_LIBVIPS" "$(npm_bin)" install -g --prefix "$(node_dir)" "${npm_args[@]}" "openclaw@next"
|
||||
requested="next"
|
||||
fi
|
||||
else
|
||||
SHARP_IGNORE_GLOBAL_LIBVIPS="$SHARP_IGNORE_GLOBAL_LIBVIPS" "$(npm_bin)" install -g --prefix "$PREFIX" "${npm_args[@]}" "openclaw@${requested}"
|
||||
SHARP_IGNORE_GLOBAL_LIBVIPS="$SHARP_IGNORE_GLOBAL_LIBVIPS" "$(npm_bin)" install -g --prefix "$(node_dir)" "${npm_args[@]}" "openclaw@${requested}"
|
||||
fi
|
||||
|
||||
mkdir -p "${PREFIX}/bin"
|
||||
rm -f "${PREFIX}/bin/openclaw"
|
||||
cat > "${PREFIX}/bin/openclaw" <<EOF
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
exec "${PREFIX}/tools/node/bin/node" "${PREFIX}/lib/node_modules/openclaw/dist/entry.js" "\$@"
|
||||
exec "${PREFIX}/tools/node/bin/node" "$(node_dir)/lib/node_modules/openclaw/dist/entry.js" "\$@"
|
||||
EOF
|
||||
chmod +x "${PREFIX}/bin/openclaw"
|
||||
emit_json "{\"event\":\"step\",\"name\":\"openclaw\",\"status\":\"ok\",\"version\":\"${requested}\"}"
|
||||
}
|
||||
|
||||
ensure_pnpm_git_prepare_allowlist() {
|
||||
local repo_dir="$1"
|
||||
local workspace_file="${repo_dir}/pnpm-workspace.yaml"
|
||||
local package_file="${repo_dir}/package.json"
|
||||
local dep="@tloncorp/api"
|
||||
local tmp
|
||||
|
||||
if [[ -f "$workspace_file" ]] && ! grep -Fq "\"${dep}\"" "$workspace_file" && ! grep -Fq -- "- ${dep}" "$workspace_file"; then
|
||||
tmp="$(mktemp)"
|
||||
if grep -q '^onlyBuiltDependencies:[[:space:]]*$' "$workspace_file"; then
|
||||
awk -v dep="$dep" '
|
||||
BEGIN { inserted = 0 }
|
||||
{
|
||||
print
|
||||
if (!inserted && $0 ~ /^onlyBuiltDependencies:[[:space:]]*$/) {
|
||||
print " - \"" dep "\""
|
||||
inserted = 1
|
||||
}
|
||||
}
|
||||
' "$workspace_file" >"$tmp"
|
||||
else
|
||||
cat "$workspace_file" >"$tmp"
|
||||
printf '\nonlyBuiltDependencies:\n - "%s"\n' "$dep" >>"$tmp"
|
||||
fi
|
||||
mv "$tmp" "$workspace_file"
|
||||
fi
|
||||
|
||||
if [[ -f "$package_file" ]]; then
|
||||
"$(node_bin)" - "$package_file" "$dep" <<'EOF'
|
||||
const fs = require("node:fs");
|
||||
|
||||
const [packageFile, dep] = process.argv.slice(2);
|
||||
const data = JSON.parse(fs.readFileSync(packageFile, "utf8"));
|
||||
const list = data.pnpm?.onlyBuiltDependencies;
|
||||
if (Array.isArray(list)) {
|
||||
if (!list.includes(dep)) {
|
||||
list.unshift(dep);
|
||||
fs.writeFileSync(packageFile, `${JSON.stringify(data, null, 2)}\n`);
|
||||
}
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (!data.pnpm || typeof data.pnpm !== "object") {
|
||||
data.pnpm = {};
|
||||
}
|
||||
data.pnpm.onlyBuiltDependencies = [dep];
|
||||
fs.writeFileSync(packageFile, `${JSON.stringify(data, null, 2)}\n`);
|
||||
EOF
|
||||
fi
|
||||
|
||||
log "Updated pnpm allowlist for git-hosted build dependency: ${dep}"
|
||||
}
|
||||
|
||||
install_openclaw_from_git() {
|
||||
local repo_dir="$1"
|
||||
local repo_url="https://github.com/openclaw/openclaw.git"
|
||||
@ -457,6 +596,7 @@ install_openclaw_from_git() {
|
||||
|
||||
ensure_git
|
||||
ensure_pnpm
|
||||
ensure_pnpm_binary_for_scripts
|
||||
|
||||
if [[ -d "$repo_dir/.git" ]]; then
|
||||
:
|
||||
@ -479,13 +619,14 @@ install_openclaw_from_git() {
|
||||
fi
|
||||
|
||||
cleanup_legacy_submodules "$repo_dir"
|
||||
ensure_pnpm_git_prepare_allowlist "$repo_dir"
|
||||
|
||||
SHARP_IGNORE_GLOBAL_LIBVIPS="$SHARP_IGNORE_GLOBAL_LIBVIPS" pnpm -C "$repo_dir" install
|
||||
SHARP_IGNORE_GLOBAL_LIBVIPS="$SHARP_IGNORE_GLOBAL_LIBVIPS" run_pnpm -C "$repo_dir" install
|
||||
|
||||
if ! pnpm -C "$repo_dir" ui:build; then
|
||||
if ! run_pnpm -C "$repo_dir" ui:build; then
|
||||
log "UI build failed; continuing (CLI may still work)"
|
||||
fi
|
||||
pnpm -C "$repo_dir" build
|
||||
run_pnpm -C "$repo_dir" build
|
||||
|
||||
mkdir -p "${PREFIX}/bin"
|
||||
cat > "${PREFIX}/bin/openclaw" <<EOF
|
||||
@ -602,4 +743,6 @@ main() {
|
||||
fi
|
||||
}
|
||||
|
||||
main "$@"
|
||||
if [[ "${OPENCLAW_INSTALL_CLI_SH_NO_RUN:-0}" != "1" ]]; then
|
||||
main "$@"
|
||||
fi
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# OpenClaw Installer for Windows
|
||||
# Usage: iwr -useb https://openclaw.ai/install.ps1 | iex
|
||||
# & ([scriptblock]::Create((iwr -useb https://openclaw.ai/install.ps1))) -Tag beta -NoOnboard -DryRun
|
||||
# Usage: powershell -c "irm https://openclaw.ai/install.ps1 | iex"
|
||||
# powershell -c "& ([scriptblock]::Create((irm https://openclaw.ai/install.ps1))) -Tag beta -NoOnboard -DryRun"
|
||||
|
||||
param(
|
||||
[string]$Tag = "latest",
|
||||
@ -14,6 +14,29 @@ param(
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$script:InstallExitCode = 0
|
||||
|
||||
function Fail-Install {
|
||||
param([int]$Code = 1)
|
||||
|
||||
$script:InstallExitCode = $Code
|
||||
return $false
|
||||
}
|
||||
|
||||
function Complete-Install {
|
||||
param([bool]$Succeeded)
|
||||
|
||||
if ($Succeeded) {
|
||||
return
|
||||
}
|
||||
|
||||
if ($PSCommandPath) {
|
||||
exit $script:InstallExitCode
|
||||
}
|
||||
|
||||
throw "OpenClaw installation failed with exit code $($script:InstallExitCode)."
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host " OpenClaw Installer" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
@ -21,7 +44,8 @@ Write-Host ""
|
||||
# Check if running in PowerShell
|
||||
if ($PSVersionTable.PSVersion.Major -lt 5) {
|
||||
Write-Host "Error: PowerShell 5+ required" -ForegroundColor Red
|
||||
exit 1
|
||||
Complete-Install -Succeeded:$false
|
||||
return
|
||||
}
|
||||
|
||||
Write-Host "[OK] Windows detected" -ForegroundColor Green
|
||||
@ -85,12 +109,17 @@ function Install-Node {
|
||||
# Try winget first (Windows 11 / Windows 10 with App Installer)
|
||||
if (Get-Command winget -ErrorAction SilentlyContinue) {
|
||||
Write-Host " Using winget..." -ForegroundColor Gray
|
||||
winget install OpenJS.NodeJS.LTS --accept-package-agreements --accept-source-agreements
|
||||
winget install OpenJS.NodeJS.LTS --source winget --accept-package-agreements --accept-source-agreements
|
||||
|
||||
# Refresh PATH
|
||||
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
|
||||
Write-Host "[OK] Node.js installed via winget" -ForegroundColor Green
|
||||
return
|
||||
if (Check-Node) {
|
||||
Write-Host "[OK] Node.js installed via winget" -ForegroundColor Green
|
||||
return $true
|
||||
}
|
||||
Write-Host "[!] winget completed, but Node.js is still unavailable in this shell" -ForegroundColor Yellow
|
||||
Write-Host "Restart PowerShell and re-run the installer if Node.js was installed successfully." -ForegroundColor Yellow
|
||||
return $false
|
||||
}
|
||||
|
||||
# Try Chocolatey
|
||||
@ -101,7 +130,7 @@ function Install-Node {
|
||||
# Refresh PATH
|
||||
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
|
||||
Write-Host "[OK] Node.js installed via Chocolatey" -ForegroundColor Green
|
||||
return
|
||||
return $true
|
||||
}
|
||||
|
||||
# Try Scoop
|
||||
@ -109,7 +138,7 @@ function Install-Node {
|
||||
Write-Host " Using Scoop..." -ForegroundColor Gray
|
||||
scoop install nodejs-lts
|
||||
Write-Host "[OK] Node.js installed via Scoop" -ForegroundColor Green
|
||||
return
|
||||
return $true
|
||||
}
|
||||
|
||||
# Manual download fallback
|
||||
@ -120,7 +149,7 @@ function Install-Node {
|
||||
Write-Host " https://nodejs.org/en/download/" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
Write-Host "Or install winget (App Installer) from the Microsoft Store." -ForegroundColor Gray
|
||||
exit 1
|
||||
return $false
|
||||
}
|
||||
|
||||
# Check for existing OpenClaw installation
|
||||
@ -141,14 +170,158 @@ function Check-Git {
|
||||
}
|
||||
}
|
||||
|
||||
function Require-Git {
|
||||
if (Check-Git) { return }
|
||||
function Add-ToProcessPath {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$PathEntry
|
||||
)
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($PathEntry)) {
|
||||
return
|
||||
}
|
||||
|
||||
$currentEntries = @($env:Path -split ";" | Where-Object { -not [string]::IsNullOrWhiteSpace($_) })
|
||||
if ($currentEntries | Where-Object { $_ -ieq $PathEntry }) {
|
||||
return
|
||||
}
|
||||
|
||||
$env:Path = "$PathEntry;$env:Path"
|
||||
}
|
||||
|
||||
function Get-PortableGitRoot {
|
||||
$base = Join-Path $env:LOCALAPPDATA "OpenClaw\deps"
|
||||
return (Join-Path $base "portable-git")
|
||||
}
|
||||
|
||||
function Get-PortableGitCommandPath {
|
||||
$root = Get-PortableGitRoot
|
||||
foreach ($candidate in @(
|
||||
(Join-Path $root "mingw64\bin\git.exe"),
|
||||
(Join-Path $root "cmd\git.exe"),
|
||||
(Join-Path $root "bin\git.exe"),
|
||||
(Join-Path $root "git.exe")
|
||||
)) {
|
||||
if (Test-Path $candidate) {
|
||||
return $candidate
|
||||
}
|
||||
}
|
||||
return $null
|
||||
}
|
||||
|
||||
function Use-PortableGitIfPresent {
|
||||
$gitExe = Get-PortableGitCommandPath
|
||||
if (-not $gitExe) {
|
||||
return $false
|
||||
}
|
||||
|
||||
$portableRoot = Get-PortableGitRoot
|
||||
foreach ($pathEntry in @(
|
||||
(Join-Path $portableRoot "mingw64\bin"),
|
||||
(Join-Path $portableRoot "usr\bin"),
|
||||
(Split-Path -Parent $gitExe)
|
||||
)) {
|
||||
if (Test-Path $pathEntry) {
|
||||
Add-ToProcessPath $pathEntry
|
||||
}
|
||||
}
|
||||
if (Check-Git) {
|
||||
return $true
|
||||
}
|
||||
return $false
|
||||
}
|
||||
|
||||
function Resolve-PortableGitDownload {
|
||||
$releaseApi = "https://api.github.com/repos/git-for-windows/git/releases/latest"
|
||||
$headers = @{
|
||||
"User-Agent" = "openclaw-installer"
|
||||
"Accept" = "application/vnd.github+json"
|
||||
}
|
||||
$release = Invoke-RestMethod -Uri $releaseApi -Headers $headers
|
||||
if (-not $release -or -not $release.assets) {
|
||||
throw "Could not resolve latest git-for-windows release metadata."
|
||||
}
|
||||
|
||||
$asset = $release.assets |
|
||||
Where-Object { $_.name -match '^MinGit-.*-64-bit\.zip$' -and $_.name -notmatch 'busybox' } |
|
||||
Select-Object -First 1
|
||||
|
||||
if (-not $asset) {
|
||||
throw "Could not find a MinGit zip asset in the latest git-for-windows release."
|
||||
}
|
||||
|
||||
return @{
|
||||
Tag = $release.tag_name
|
||||
Name = $asset.name
|
||||
Url = $asset.browser_download_url
|
||||
}
|
||||
}
|
||||
|
||||
function Install-PortableGit {
|
||||
if (Use-PortableGitIfPresent) {
|
||||
$portableVersion = (& git --version 2>$null)
|
||||
if ($portableVersion) {
|
||||
Write-Host "[OK] User-local Git already available: $portableVersion" -ForegroundColor Green
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
Write-Host "[*] Git not found; bootstrapping user-local portable Git..." -ForegroundColor Yellow
|
||||
|
||||
$download = Resolve-PortableGitDownload
|
||||
$portableRoot = Get-PortableGitRoot
|
||||
$portableParent = Split-Path -Parent $portableRoot
|
||||
$tmpZip = Join-Path $env:TEMP $download.Name
|
||||
$tmpExtract = Join-Path $env:TEMP ("openclaw-portable-git-" + [guid]::NewGuid().ToString("N"))
|
||||
|
||||
New-Item -ItemType Directory -Force -Path $portableParent | Out-Null
|
||||
if (Test-Path $portableRoot) {
|
||||
Remove-Item -Recurse -Force $portableRoot
|
||||
}
|
||||
if (Test-Path $tmpExtract) {
|
||||
Remove-Item -Recurse -Force $tmpExtract
|
||||
}
|
||||
New-Item -ItemType Directory -Force -Path $tmpExtract | Out-Null
|
||||
|
||||
try {
|
||||
Write-Host " Downloading $($download.Tag)..." -ForegroundColor Gray
|
||||
Invoke-WebRequest -Uri $download.Url -OutFile $tmpZip
|
||||
Expand-Archive -Path $tmpZip -DestinationPath $tmpExtract -Force
|
||||
Move-Item -Path (Join-Path $tmpExtract "*") -Destination $portableRoot -Force
|
||||
} finally {
|
||||
if (Test-Path $tmpZip) {
|
||||
Remove-Item -Force $tmpZip
|
||||
}
|
||||
if (Test-Path $tmpExtract) {
|
||||
Remove-Item -Recurse -Force $tmpExtract
|
||||
}
|
||||
}
|
||||
|
||||
if (-not (Use-PortableGitIfPresent)) {
|
||||
throw "Portable Git bootstrap completed, but git is still unavailable."
|
||||
}
|
||||
|
||||
$portableVersion = (& git --version 2>$null)
|
||||
Write-Host "[OK] User-local Git ready: $portableVersion" -ForegroundColor Green
|
||||
}
|
||||
|
||||
function Ensure-Git {
|
||||
if (Check-Git) { return $true }
|
||||
if (Use-PortableGitIfPresent) { return $true }
|
||||
try {
|
||||
Install-PortableGit
|
||||
if (Check-Git) {
|
||||
return $true
|
||||
}
|
||||
} catch {
|
||||
Write-Host "[!] Portable Git bootstrap failed: $($_.Exception.Message)" -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Error: Git is required to install OpenClaw." -ForegroundColor Red
|
||||
Write-Host "Install Git for Windows:" -ForegroundColor Yellow
|
||||
Write-Host "Auto-bootstrap of user-local Git did not succeed." -ForegroundColor Yellow
|
||||
Write-Host "Install Git for Windows manually, then re-run this installer:" -ForegroundColor Yellow
|
||||
Write-Host " https://git-scm.com/download/win" -ForegroundColor Cyan
|
||||
Write-Host "Then re-run this installer." -ForegroundColor Yellow
|
||||
exit 1
|
||||
return $false
|
||||
}
|
||||
|
||||
function Get-OpenClawCommandPath {
|
||||
@ -179,6 +352,38 @@ function Invoke-OpenClawCommand {
|
||||
& $commandPath @Arguments
|
||||
}
|
||||
|
||||
function Resolve-CommandPath {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string[]]$Candidates
|
||||
)
|
||||
|
||||
foreach ($candidate in $Candidates) {
|
||||
$command = Get-Command $candidate -ErrorAction SilentlyContinue
|
||||
if ($command -and $command.Source) {
|
||||
return $command.Source
|
||||
}
|
||||
}
|
||||
|
||||
return $null
|
||||
}
|
||||
|
||||
function Get-NpmCommandPath {
|
||||
$path = Resolve-CommandPath -Candidates @("npm.cmd", "npm.exe", "npm")
|
||||
if (-not $path) {
|
||||
throw "npm not found on PATH."
|
||||
}
|
||||
return $path
|
||||
}
|
||||
|
||||
function Get-CorepackCommandPath {
|
||||
return (Resolve-CommandPath -Candidates @("corepack.cmd", "corepack.exe", "corepack"))
|
||||
}
|
||||
|
||||
function Get-PnpmCommandPath {
|
||||
return (Resolve-CommandPath -Candidates @("pnpm.cmd", "pnpm.exe", "pnpm"))
|
||||
}
|
||||
|
||||
function Get-NpmGlobalBinCandidates {
|
||||
param(
|
||||
[string]$NpmPrefix
|
||||
@ -203,7 +408,7 @@ function Ensure-OpenClawOnPath {
|
||||
|
||||
$npmPrefix = $null
|
||||
try {
|
||||
$npmPrefix = (npm config get prefix 2>$null).Trim()
|
||||
$npmPrefix = (& (Get-NpmCommandPath) config get prefix 2>$null).Trim()
|
||||
} catch {
|
||||
$npmPrefix = $null
|
||||
}
|
||||
@ -237,14 +442,15 @@ function Ensure-OpenClawOnPath {
|
||||
}
|
||||
|
||||
function Ensure-Pnpm {
|
||||
if (Get-Command pnpm -ErrorAction SilentlyContinue) {
|
||||
if (Get-PnpmCommandPath) {
|
||||
return
|
||||
}
|
||||
if (Get-Command corepack -ErrorAction SilentlyContinue) {
|
||||
$corepackCommand = Get-CorepackCommandPath
|
||||
if ($corepackCommand) {
|
||||
try {
|
||||
corepack enable | Out-Null
|
||||
corepack prepare pnpm@latest --activate | Out-Null
|
||||
if (Get-Command pnpm -ErrorAction SilentlyContinue) {
|
||||
& $corepackCommand enable | Out-Null
|
||||
& $corepackCommand prepare pnpm@latest --activate | Out-Null
|
||||
if (Get-PnpmCommandPath) {
|
||||
Write-Host "[OK] pnpm installed via corepack" -ForegroundColor Green
|
||||
return
|
||||
}
|
||||
@ -256,7 +462,7 @@ function Ensure-Pnpm {
|
||||
$prevScriptShell = $env:NPM_CONFIG_SCRIPT_SHELL
|
||||
$env:NPM_CONFIG_SCRIPT_SHELL = "cmd.exe"
|
||||
try {
|
||||
npm install -g pnpm
|
||||
& (Get-NpmCommandPath) install -g pnpm
|
||||
} finally {
|
||||
$env:NPM_CONFIG_SCRIPT_SHELL = $prevScriptShell
|
||||
}
|
||||
@ -264,30 +470,60 @@ function Ensure-Pnpm {
|
||||
}
|
||||
|
||||
# Install OpenClaw
|
||||
function Resolve-NpmOpenClawInstallSpec {
|
||||
param(
|
||||
[string]$PackageName,
|
||||
[string]$RequestedTag
|
||||
)
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($RequestedTag)) {
|
||||
return "$PackageName@latest"
|
||||
}
|
||||
|
||||
$trimmedTag = $RequestedTag.Trim()
|
||||
if (
|
||||
$trimmedTag -match '^(https?|file):' -or
|
||||
$trimmedTag -match '^(git\+|github:)' -or
|
||||
$trimmedTag -match '^[A-Za-z]:[\\/]' -or
|
||||
$trimmedTag -match '^\\\\' -or
|
||||
$trimmedTag -match '^\.\.?[\\/]' -or
|
||||
$trimmedTag -match '\.tgz($|[?#])'
|
||||
) {
|
||||
return $trimmedTag
|
||||
}
|
||||
|
||||
return "$PackageName@$trimmedTag"
|
||||
}
|
||||
|
||||
function Install-OpenClaw {
|
||||
if ([string]::IsNullOrWhiteSpace($Tag)) {
|
||||
$Tag = "latest"
|
||||
}
|
||||
Require-Git
|
||||
if (-not (Ensure-Git)) {
|
||||
return $false
|
||||
}
|
||||
|
||||
# Use openclaw package for beta, openclaw for stable
|
||||
$packageName = "openclaw"
|
||||
if ($Tag -eq "beta" -or $Tag -match "^beta\.") {
|
||||
$packageName = "openclaw"
|
||||
}
|
||||
Write-Host "[*] Installing OpenClaw ($packageName@$Tag)..." -ForegroundColor Yellow
|
||||
$installSpec = Resolve-NpmOpenClawInstallSpec -PackageName $packageName -RequestedTag $Tag
|
||||
Write-Host "[*] Installing OpenClaw ($installSpec)..." -ForegroundColor Yellow
|
||||
$prevLogLevel = $env:NPM_CONFIG_LOGLEVEL
|
||||
$prevUpdateNotifier = $env:NPM_CONFIG_UPDATE_NOTIFIER
|
||||
$prevFund = $env:NPM_CONFIG_FUND
|
||||
$prevAudit = $env:NPM_CONFIG_AUDIT
|
||||
$prevScriptShell = $env:NPM_CONFIG_SCRIPT_SHELL
|
||||
$prevNodeLlamaSkipDownload = $env:NODE_LLAMA_CPP_SKIP_DOWNLOAD
|
||||
$env:NPM_CONFIG_LOGLEVEL = "error"
|
||||
$env:NPM_CONFIG_UPDATE_NOTIFIER = "false"
|
||||
$env:NPM_CONFIG_FUND = "false"
|
||||
$env:NPM_CONFIG_AUDIT = "false"
|
||||
$env:NPM_CONFIG_SCRIPT_SHELL = "cmd.exe"
|
||||
$env:NODE_LLAMA_CPP_SKIP_DOWNLOAD = "1"
|
||||
try {
|
||||
$npmOutput = npm install -g "$packageName@$Tag" 2>&1
|
||||
$npmOutput = & (Get-NpmCommandPath) install -g "$installSpec" 2>&1
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host "[!] npm install failed" -ForegroundColor Red
|
||||
if ($npmOutput -match "spawn git" -or $npmOutput -match "ENOENT.*git") {
|
||||
@ -296,10 +532,10 @@ function Install-OpenClaw {
|
||||
Write-Host " https://git-scm.com/download/win" -ForegroundColor Cyan
|
||||
} else {
|
||||
Write-Host "Re-run with verbose output to see the full error:" -ForegroundColor Yellow
|
||||
Write-Host " iwr -useb https://openclaw.ai/install.ps1 | iex" -ForegroundColor Cyan
|
||||
Write-Host ' powershell -c "irm https://openclaw.ai/install.ps1 | iex"' -ForegroundColor Cyan
|
||||
}
|
||||
$npmOutput | ForEach-Object { Write-Host $_ }
|
||||
exit 1
|
||||
return $false
|
||||
}
|
||||
} finally {
|
||||
$env:NPM_CONFIG_LOGLEVEL = $prevLogLevel
|
||||
@ -307,8 +543,10 @@ function Install-OpenClaw {
|
||||
$env:NPM_CONFIG_FUND = $prevFund
|
||||
$env:NPM_CONFIG_AUDIT = $prevAudit
|
||||
$env:NPM_CONFIG_SCRIPT_SHELL = $prevScriptShell
|
||||
$env:NODE_LLAMA_CPP_SKIP_DOWNLOAD = $prevNodeLlamaSkipDownload
|
||||
}
|
||||
Write-Host "[OK] OpenClaw installed" -ForegroundColor Green
|
||||
return $true
|
||||
}
|
||||
|
||||
# Install OpenClaw from GitHub
|
||||
@ -317,7 +555,9 @@ function Install-OpenClawFromGit {
|
||||
[string]$RepoDir,
|
||||
[switch]$SkipUpdate
|
||||
)
|
||||
Require-Git
|
||||
if (-not (Ensure-Git)) {
|
||||
return $false
|
||||
}
|
||||
Ensure-Pnpm
|
||||
|
||||
$repoUrl = "https://github.com/openclaw/openclaw.git"
|
||||
@ -340,13 +580,17 @@ function Install-OpenClawFromGit {
|
||||
Remove-LegacySubmodule -RepoDir $RepoDir
|
||||
|
||||
$prevPnpmScriptShell = $env:NPM_CONFIG_SCRIPT_SHELL
|
||||
$pnpmCommand = Get-PnpmCommandPath
|
||||
if (-not $pnpmCommand) {
|
||||
throw "pnpm not found after installation."
|
||||
}
|
||||
$env:NPM_CONFIG_SCRIPT_SHELL = "cmd.exe"
|
||||
try {
|
||||
pnpm -C $RepoDir install
|
||||
if (-not (pnpm -C $RepoDir ui:build)) {
|
||||
& $pnpmCommand -C $RepoDir install
|
||||
if (-not (& $pnpmCommand -C $RepoDir ui:build)) {
|
||||
Write-Host "[!] UI build failed; continuing (CLI may still work)" -ForegroundColor Yellow
|
||||
}
|
||||
pnpm -C $RepoDir build
|
||||
& $pnpmCommand -C $RepoDir build
|
||||
} finally {
|
||||
$env:NPM_CONFIG_SCRIPT_SHELL = $prevPnpmScriptShell
|
||||
}
|
||||
@ -368,6 +612,7 @@ function Install-OpenClawFromGit {
|
||||
|
||||
Write-Host "[OK] OpenClaw wrapper installed to $cmdPath" -ForegroundColor Green
|
||||
Write-Host "[i] This checkout uses pnpm. For deps, run: pnpm install (avoid npm install in the repo)." -ForegroundColor Gray
|
||||
return $true
|
||||
}
|
||||
|
||||
# Run doctor for migrations (safe, non-interactive)
|
||||
@ -415,7 +660,7 @@ function Refresh-GatewayServiceIfLoaded {
|
||||
|
||||
try {
|
||||
Invoke-OpenClawCommand gateway restart | Out-Null
|
||||
Invoke-OpenClawCommand gateway status --probe --json | Out-Null
|
||||
Invoke-OpenClawCommand gateway status --json | Out-Null
|
||||
Write-Host "[OK] Gateway service refreshed" -ForegroundColor Green
|
||||
} catch {
|
||||
Write-Host "[!] Gateway service restart failed; continuing." -ForegroundColor Yellow
|
||||
@ -448,7 +693,7 @@ function Remove-LegacySubmodule {
|
||||
function Main {
|
||||
if ($InstallMethod -ne "npm" -and $InstallMethod -ne "git") {
|
||||
Write-Host "Error: invalid -InstallMethod (use npm or git)." -ForegroundColor Red
|
||||
exit 2
|
||||
return (Fail-Install -Code 2)
|
||||
}
|
||||
|
||||
if ($DryRun) {
|
||||
@ -465,7 +710,7 @@ function Main {
|
||||
if ($NoOnboard) {
|
||||
Write-Host "[OK] Onboard: skipped" -ForegroundColor Green
|
||||
}
|
||||
return
|
||||
return $true
|
||||
}
|
||||
|
||||
Remove-LegacySubmodule -RepoDir $RepoDir
|
||||
@ -475,14 +720,16 @@ function Main {
|
||||
|
||||
# Step 1: Node.js
|
||||
if (-not (Check-Node)) {
|
||||
Install-Node
|
||||
if (-not (Install-Node)) {
|
||||
return (Fail-Install)
|
||||
}
|
||||
|
||||
# Verify installation
|
||||
if (-not (Check-Node)) {
|
||||
Write-Host ""
|
||||
Write-Host "Error: Node.js installation may require a terminal restart" -ForegroundColor Red
|
||||
Write-Host "Please close this terminal, open a new one, and run this installer again." -ForegroundColor Yellow
|
||||
exit 1
|
||||
return (Fail-Install)
|
||||
}
|
||||
}
|
||||
|
||||
@ -490,10 +737,26 @@ function Main {
|
||||
|
||||
# Step 2: OpenClaw
|
||||
if ($InstallMethod -eq "git") {
|
||||
try {
|
||||
$npmCommand = Get-NpmCommandPath
|
||||
if ($npmCommand) {
|
||||
& $npmCommand uninstall -g openclaw 2>$null | Out-Null
|
||||
Write-Host "[OK] Removed npm global install if present" -ForegroundColor Green
|
||||
}
|
||||
} catch { }
|
||||
$finalGitDir = $GitDir
|
||||
Install-OpenClawFromGit -RepoDir $GitDir -SkipUpdate:$NoGitUpdate
|
||||
if (-not (Install-OpenClawFromGit -RepoDir $GitDir -SkipUpdate:$NoGitUpdate)) {
|
||||
return (Fail-Install)
|
||||
}
|
||||
} else {
|
||||
Install-OpenClaw
|
||||
$gitWrapper = Join-Path (Join-Path $env:USERPROFILE ".local\\bin") "openclaw.cmd"
|
||||
if (Test-Path $gitWrapper) {
|
||||
Remove-Item -Force $gitWrapper
|
||||
Write-Host "[OK] Removed git wrapper (switching to npm)" -ForegroundColor Green
|
||||
}
|
||||
if (-not (Install-OpenClaw)) {
|
||||
return (Fail-Install)
|
||||
}
|
||||
}
|
||||
|
||||
if (-not (Ensure-OpenClawOnPath)) {
|
||||
@ -517,7 +780,7 @@ function Main {
|
||||
}
|
||||
if (-not $installedVersion) {
|
||||
try {
|
||||
$npmList = npm list -g --depth 0 --json 2>$null | ConvertFrom-Json
|
||||
$npmList = & (Get-NpmCommandPath) list -g --depth 0 --json 2>$null | ConvertFrom-Json
|
||||
if ($npmList -and $npmList.dependencies -and $npmList.dependencies.openclaw -and $npmList.dependencies.openclaw.version) {
|
||||
$installedVersion = $npmList.dependencies.openclaw.version
|
||||
}
|
||||
@ -596,6 +859,10 @@ function Main {
|
||||
Invoke-OpenClawCommand onboard
|
||||
}
|
||||
}
|
||||
|
||||
return $true
|
||||
}
|
||||
|
||||
Main
|
||||
$mainResults = @(Main)
|
||||
$installSucceeded = $mainResults.Count -gt 0 -and $mainResults[-1] -eq $true
|
||||
Complete-Install -Succeeded:$installSucceeded
|
||||
|
||||
@ -19,6 +19,31 @@ DEFAULT_TAGLINE="All your chats, one OpenClaw."
|
||||
|
||||
ORIGINAL_PATH="${PATH:-}"
|
||||
|
||||
ensure_home_env() {
|
||||
if [[ -n "${HOME:-}" && "${HOME}" != "/" && -d "${HOME}" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
local user_name=""
|
||||
local home_dir=""
|
||||
user_name="$(id -un 2>/dev/null || true)"
|
||||
|
||||
if [[ -n "$user_name" ]]; then
|
||||
if command -v getent >/dev/null 2>&1; then
|
||||
home_dir="$(getent passwd "$user_name" 2>/dev/null | awk -F: '{print $6; exit}' || true)"
|
||||
fi
|
||||
if [[ -z "$home_dir" && "$(uname -s 2>/dev/null || true)" == "Darwin" ]] && command -v dscl >/dev/null 2>&1; then
|
||||
home_dir="$(dscl . -read "/Users/${user_name}" NFSHomeDirectory 2>/dev/null | awk '{print $2; exit}' || true)"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "$home_dir" && "$home_dir" != "/" && -d "$home_dir" ]]; then
|
||||
export HOME="$home_dir"
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_home_env
|
||||
|
||||
TMPFILES=()
|
||||
cleanup_tmpfiles() {
|
||||
local f
|
||||
@ -35,6 +60,38 @@ mktempfile() {
|
||||
echo "$f"
|
||||
}
|
||||
|
||||
resolve_brew_bin() {
|
||||
local brew_bin=""
|
||||
brew_bin="$(command -v brew 2>/dev/null || true)"
|
||||
if [[ -n "$brew_bin" ]]; then
|
||||
echo "$brew_bin"
|
||||
return 0
|
||||
fi
|
||||
if [[ -x "/opt/homebrew/bin/brew" ]]; then
|
||||
echo "/opt/homebrew/bin/brew"
|
||||
return 0
|
||||
fi
|
||||
if [[ -x "/usr/local/bin/brew" ]]; then
|
||||
echo "/usr/local/bin/brew"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
activate_brew_for_session() {
|
||||
local brew_bin=""
|
||||
brew_bin="$(resolve_brew_bin || true)"
|
||||
if [[ -z "$brew_bin" ]]; then
|
||||
return 1
|
||||
fi
|
||||
if [[ -z "$(command -v brew 2>/dev/null || true)" && "${BREW_SHELLENV_ANNOUNCED:-0}" != "1" ]]; then
|
||||
ui_info "Found Homebrew at ${brew_bin}; exporting shellenv"
|
||||
BREW_SHELLENV_ANNOUNCED=1
|
||||
fi
|
||||
eval "$("$brew_bin" shellenv)"
|
||||
return 0
|
||||
}
|
||||
|
||||
DOWNLOADER=""
|
||||
detect_downloader() {
|
||||
if command -v curl &> /dev/null; then
|
||||
@ -258,7 +315,7 @@ detect_os_or_die() {
|
||||
if [[ "$OS" == "unknown" ]]; then
|
||||
ui_error "Unsupported operating system"
|
||||
echo "This installer supports macOS and Linux (including WSL)."
|
||||
echo "For Windows, use: iwr -useb https://openclaw.ai/install.ps1 | iex"
|
||||
echo "For Windows, use: powershell -c \"irm https://openclaw.ai/install.ps1 | iex\""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@ -544,10 +601,10 @@ install_build_tools_linux() {
|
||||
if command -v apt-get &> /dev/null; then
|
||||
if is_root; then
|
||||
run_quiet_step "Updating package index" apt-get update -qq
|
||||
run_quiet_step "Installing build tools" apt-get install -y -qq build-essential python3 make g++ cmake
|
||||
run_quiet_step "Installing build tools" env DEBIAN_FRONTEND=noninteractive apt-get install -y -qq build-essential python3 make g++ cmake
|
||||
else
|
||||
run_quiet_step "Updating package index" sudo apt-get update -qq
|
||||
run_quiet_step "Installing build tools" sudo apt-get install -y -qq build-essential python3 make g++ cmake
|
||||
run_quiet_step "Installing build tools" sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y -qq build-essential python3 make g++ cmake
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
@ -585,6 +642,7 @@ install_build_tools_linux() {
|
||||
|
||||
install_build_tools_macos() {
|
||||
local ok=true
|
||||
local brew_bin=""
|
||||
|
||||
if ! xcode-select -p >/dev/null 2>&1; then
|
||||
ui_info "Installing Xcode Command Line Tools (required for make/clang)"
|
||||
@ -597,8 +655,10 @@ install_build_tools_macos() {
|
||||
fi
|
||||
|
||||
if ! command -v cmake >/dev/null 2>&1; then
|
||||
if command -v brew >/dev/null 2>&1; then
|
||||
run_quiet_step "Installing cmake" brew install cmake
|
||||
brew_bin="$(resolve_brew_bin || true)"
|
||||
if [[ -n "$brew_bin" ]]; then
|
||||
activate_brew_for_session || true
|
||||
run_quiet_step "Installing cmake" "$brew_bin" install cmake
|
||||
else
|
||||
ui_warn "Homebrew not available; cannot auto-install cmake"
|
||||
ok=false
|
||||
@ -958,6 +1018,9 @@ SHARP_IGNORE_GLOBAL_LIBVIPS="${SHARP_IGNORE_GLOBAL_LIBVIPS:-1}"
|
||||
NPM_LOGLEVEL="${OPENCLAW_NPM_LOGLEVEL:-error}"
|
||||
NPM_SILENT_FLAG="--silent"
|
||||
VERBOSE="${OPENCLAW_VERBOSE:-0}"
|
||||
INSTALL_PROFILE="${OPENCLAW_PROFILE:-}"
|
||||
INSTALL_WORKSPACE="${OPENCLAW_WORKSPACE:-}"
|
||||
INSTALL_GATEWAY_PORT="${OPENCLAW_GATEWAY_PORT:-}"
|
||||
OPENCLAW_BIN=""
|
||||
SELECTED_NODE_BIN=""
|
||||
PNPM_CMD=()
|
||||
@ -977,6 +1040,9 @@ Options:
|
||||
--version <version|dist-tag> npm install: version (default: latest)
|
||||
--beta Use beta if available, else latest
|
||||
--git-dir, --dir <path> Checkout directory (default: ~/openclaw)
|
||||
--profile <name> Isolated OpenClaw profile for config/state/service naming
|
||||
--workspace <dir> Workspace to use during onboarding
|
||||
--gateway-port <port> Gateway port to write during onboarding
|
||||
--no-git-update Skip git pull for existing checkout
|
||||
--no-onboard Skip onboarding (non-interactive)
|
||||
--no-prompt Disable prompts (required in CI/automation)
|
||||
@ -990,6 +1056,9 @@ Environment variables:
|
||||
OPENCLAW_BETA=0|1
|
||||
OPENCLAW_GIT_DIR=...
|
||||
OPENCLAW_GIT_UPDATE=0|1
|
||||
OPENCLAW_PROFILE=<name>
|
||||
OPENCLAW_WORKSPACE=<dir>
|
||||
OPENCLAW_GATEWAY_PORT=<port>
|
||||
OPENCLAW_NO_PROMPT=1
|
||||
OPENCLAW_DRY_RUN=1
|
||||
OPENCLAW_NO_ONBOARD=1
|
||||
@ -1001,6 +1070,7 @@ Examples:
|
||||
curl -fsSL --proto '=https' --tlsv1.2 https://openclaw.ai/install.sh | bash
|
||||
curl -fsSL --proto '=https' --tlsv1.2 https://openclaw.ai/install.sh | bash -s -- --no-onboard
|
||||
curl -fsSL --proto '=https' --tlsv1.2 https://openclaw.ai/install.sh | bash -s -- --install-method git --no-onboard
|
||||
curl -fsSL --proto '=https' --tlsv1.2 https://openclaw.ai/install.sh | OPENCLAW_PROFILE=rescue bash -s -- --profile rescue --gateway-port 19001 --workspace ~/openclaw-rescue
|
||||
EOF
|
||||
}
|
||||
|
||||
@ -1055,6 +1125,18 @@ parse_args() {
|
||||
GIT_DIR="$2"
|
||||
shift 2
|
||||
;;
|
||||
--profile)
|
||||
INSTALL_PROFILE="$2"
|
||||
shift 2
|
||||
;;
|
||||
--workspace)
|
||||
INSTALL_WORKSPACE="$2"
|
||||
shift 2
|
||||
;;
|
||||
--gateway-port)
|
||||
INSTALL_GATEWAY_PORT="$2"
|
||||
shift 2
|
||||
;;
|
||||
--no-git-update)
|
||||
GIT_UPDATE=0
|
||||
shift
|
||||
@ -1192,8 +1274,10 @@ print_homebrew_admin_fix() {
|
||||
}
|
||||
|
||||
install_homebrew() {
|
||||
local brew_bin=""
|
||||
if [[ "$OS" == "macos" ]]; then
|
||||
if ! command -v brew &> /dev/null; then
|
||||
brew_bin="$(resolve_brew_bin || true)"
|
||||
if [[ -z "$brew_bin" ]]; then
|
||||
if ! is_macos_admin_user; then
|
||||
print_homebrew_admin_fix
|
||||
exit 1
|
||||
@ -1202,13 +1286,12 @@ install_homebrew() {
|
||||
run_quiet_step "Installing Homebrew" run_remote_bash "https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh"
|
||||
|
||||
# Add Homebrew to PATH for this session
|
||||
if [[ -f "/opt/homebrew/bin/brew" ]]; then
|
||||
eval "$(/opt/homebrew/bin/brew shellenv)"
|
||||
elif [[ -f "/usr/local/bin/brew" ]]; then
|
||||
eval "$(/usr/local/bin/brew shellenv)"
|
||||
if ! activate_brew_for_session; then
|
||||
ui_warn "Homebrew install completed but brew is still unavailable in this shell"
|
||||
fi
|
||||
ui_success "Homebrew installed"
|
||||
else
|
||||
activate_brew_for_session || true
|
||||
ui_success "Homebrew already installed"
|
||||
fi
|
||||
fi
|
||||
@ -1252,9 +1335,12 @@ ensure_macos_node22_active() {
|
||||
return 0
|
||||
fi
|
||||
|
||||
local brew_bin=""
|
||||
local brew_node_prefix=""
|
||||
if command -v brew &> /dev/null; then
|
||||
brew_node_prefix="$(brew --prefix node@22 2>/dev/null || true)"
|
||||
brew_bin="$(resolve_brew_bin || true)"
|
||||
if [[ -n "$brew_bin" ]]; then
|
||||
activate_brew_for_session || true
|
||||
brew_node_prefix="$("$brew_bin" --prefix node@22 2>/dev/null || true)"
|
||||
if [[ -n "$brew_node_prefix" && -x "${brew_node_prefix}/bin/node" ]]; then
|
||||
export PATH="${brew_node_prefix}/bin:$PATH"
|
||||
refresh_shell_command_cache
|
||||
@ -1857,6 +1943,148 @@ npm_global_bin_dir() {
|
||||
return 1
|
||||
}
|
||||
|
||||
canonicalize_dir() {
|
||||
local dir="$1"
|
||||
if [[ -z "$dir" || ! -d "$dir" ]]; then
|
||||
return 1
|
||||
fi
|
||||
(cd "$dir" 2>/dev/null && pwd -P) || return 1
|
||||
}
|
||||
|
||||
openclaw_package_version() {
|
||||
local package_json="$1"
|
||||
if [[ ! -f "$package_json" ]]; then
|
||||
echo "unknown"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local version=""
|
||||
if command -v node >/dev/null 2>&1; then
|
||||
version="$(node -e 'const fs = require("fs"); const pkg = JSON.parse(fs.readFileSync(process.argv[1], "utf8")); process.stdout.write(String(pkg.version || "unknown"));' "$package_json" 2>/dev/null || true)"
|
||||
fi
|
||||
if [[ -z "$version" ]]; then
|
||||
version="$(sed -n -E 's/^[[:space:]]*"version"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/p' "$package_json" | head -n1)"
|
||||
fi
|
||||
echo "${version:-unknown}"
|
||||
}
|
||||
|
||||
emit_npm_root_candidate() {
|
||||
local root="${1%/}"
|
||||
if [[ -n "$root" && "$root" == /* ]]; then
|
||||
echo "$root"
|
||||
fi
|
||||
}
|
||||
|
||||
collect_openclaw_npm_root_candidates() {
|
||||
local root=""
|
||||
root="$(npm root -g 2>/dev/null || true)"
|
||||
emit_npm_root_candidate "$root"
|
||||
|
||||
local npm_cmd=""
|
||||
while IFS= read -r npm_cmd; do
|
||||
[[ -n "$npm_cmd" ]] || continue
|
||||
root="$("$npm_cmd" root -g 2>/dev/null || true)"
|
||||
emit_npm_root_candidate "$root"
|
||||
done < <(type -aP npm 2>/dev/null | awk '!seen[$0]++' || true)
|
||||
|
||||
local extra_root=""
|
||||
local old_ifs="$IFS"
|
||||
IFS=":"
|
||||
for extra_root in ${OPENCLAW_INSTALL_EXTRA_NPM_ROOTS:-}; do
|
||||
emit_npm_root_candidate "$extra_root"
|
||||
done
|
||||
IFS="$old_ifs"
|
||||
|
||||
emit_npm_root_candidate "/opt/homebrew/lib/node_modules"
|
||||
emit_npm_root_candidate "/usr/local/lib/node_modules"
|
||||
emit_npm_root_candidate "/usr/lib/node_modules"
|
||||
|
||||
local manager_dir=""
|
||||
local candidate=""
|
||||
for manager_dir in "${NVM_DIR:-}" "$HOME/.nvm"; do
|
||||
[[ -n "$manager_dir" && -d "$manager_dir" ]] || continue
|
||||
for candidate in "$manager_dir"/versions/node/*/lib/node_modules; do
|
||||
[[ -d "$candidate" ]] && emit_npm_root_candidate "$candidate"
|
||||
done
|
||||
done
|
||||
|
||||
for manager_dir in "${FNM_DIR:-}" "$HOME/.fnm" "$HOME/.local/share/fnm"; do
|
||||
[[ -n "$manager_dir" && -d "$manager_dir" ]] || continue
|
||||
for candidate in "$manager_dir"/node-versions/*/installation/lib/node_modules; do
|
||||
[[ -d "$candidate" ]] && emit_npm_root_candidate "$candidate"
|
||||
done
|
||||
done
|
||||
|
||||
for manager_dir in "${VOLTA_HOME:-}" "$HOME/.volta"; do
|
||||
[[ -n "$manager_dir" && -d "$manager_dir" ]] || continue
|
||||
for candidate in "$manager_dir"/tools/image/node/*/lib/node_modules; do
|
||||
[[ -d "$candidate" ]] && emit_npm_root_candidate "$candidate"
|
||||
done
|
||||
done
|
||||
}
|
||||
|
||||
find_openclaw_global_installs() {
|
||||
local seen="|"
|
||||
local npm_root=""
|
||||
while IFS= read -r npm_root; do
|
||||
[[ -n "$npm_root" ]] || continue
|
||||
local package_dir="${npm_root%/}/openclaw"
|
||||
local package_json="${package_dir}/package.json"
|
||||
[[ -f "$package_json" ]] || continue
|
||||
|
||||
local real_package_dir=""
|
||||
real_package_dir="$(canonicalize_dir "$package_dir" || true)"
|
||||
[[ -n "$real_package_dir" ]] || real_package_dir="$package_dir"
|
||||
case "$seen" in
|
||||
*"|${real_package_dir}|"*) continue ;;
|
||||
esac
|
||||
seen="${seen}${real_package_dir}|"
|
||||
|
||||
local version=""
|
||||
version="$(openclaw_package_version "$package_json")"
|
||||
printf '%s\t%s\t%s\n' "$version" "$real_package_dir" "$npm_root"
|
||||
done < <(collect_openclaw_npm_root_candidates)
|
||||
}
|
||||
|
||||
warn_duplicate_openclaw_global_installs() {
|
||||
local installs=()
|
||||
local line=""
|
||||
while IFS= read -r line; do
|
||||
[[ -n "$line" ]] && installs+=("$line")
|
||||
done < <(find_openclaw_global_installs)
|
||||
|
||||
if [[ "${#installs[@]}" -le 1 ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
ui_warn "Multiple OpenClaw global installs detected"
|
||||
echo " Different Node/npm environments can run different OpenClaw versions."
|
||||
|
||||
local active_node active_npm active_openclaw
|
||||
active_node="$(command -v node 2>/dev/null || true)"
|
||||
active_npm="$(command -v npm 2>/dev/null || true)"
|
||||
active_openclaw="${OPENCLAW_BIN:-}"
|
||||
if [[ -z "$active_openclaw" ]]; then
|
||||
active_openclaw="$(type -P openclaw 2>/dev/null || true)"
|
||||
fi
|
||||
echo -e " Active node: ${INFO}${active_node:-none}${NC}"
|
||||
echo -e " Active npm: ${INFO}${active_npm:-none}${NC}"
|
||||
echo -e " Active openclaw: ${INFO}${active_openclaw:-none}${NC}"
|
||||
echo ""
|
||||
echo " Found installs:"
|
||||
|
||||
local install version package_dir npm_root
|
||||
for install in "${installs[@]}"; do
|
||||
IFS=$'\t' read -r version package_dir npm_root <<< "$install"
|
||||
echo -e " - ${INFO}${version:-unknown}${NC} ${package_dir}"
|
||||
echo -e " npm root: ${MUTED}${npm_root}${NC}"
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo " Keep one install source, then remove stale installs with that environment's npm:"
|
||||
echo " npm uninstall -g openclaw"
|
||||
}
|
||||
|
||||
refresh_shell_command_cache() {
|
||||
hash -r 2>/dev/null || true
|
||||
}
|
||||
@ -2000,6 +2228,7 @@ install_openclaw_from_git() {
|
||||
fi
|
||||
|
||||
cleanup_legacy_submodules "$repo_dir"
|
||||
ensure_pnpm_git_prepare_allowlist "$repo_dir"
|
||||
|
||||
SHARP_IGNORE_GLOBAL_LIBVIPS="$SHARP_IGNORE_GLOBAL_LIBVIPS" run_quiet_step "Installing dependencies" run_pnpm -C "$repo_dir" install
|
||||
|
||||
@ -2020,6 +2249,59 @@ EOF
|
||||
ui_info "This checkout uses pnpm — run pnpm install (or corepack pnpm install) for deps"
|
||||
}
|
||||
|
||||
ensure_pnpm_git_prepare_allowlist() {
|
||||
local repo_dir="$1"
|
||||
local workspace_file="${repo_dir}/pnpm-workspace.yaml"
|
||||
local package_file="${repo_dir}/package.json"
|
||||
local dep="@tloncorp/api"
|
||||
local tmp=""
|
||||
|
||||
if [[ -f "$workspace_file" ]] && ! grep -Fq "\"${dep}\"" "$workspace_file" && ! grep -Fq -- "- ${dep}" "$workspace_file"; then
|
||||
tmp="$(mktemp)"
|
||||
if grep -q '^onlyBuiltDependencies:[[:space:]]*$' "$workspace_file"; then
|
||||
awk -v dep="$dep" '
|
||||
BEGIN { inserted = 0 }
|
||||
{
|
||||
print
|
||||
if (!inserted && $0 ~ /^onlyBuiltDependencies:[[:space:]]*$/) {
|
||||
print " - \"" dep "\""
|
||||
inserted = 1
|
||||
}
|
||||
}
|
||||
' "$workspace_file" >"$tmp"
|
||||
else
|
||||
cat "$workspace_file" >"$tmp"
|
||||
printf '\nonlyBuiltDependencies:\n - "%s"\n' "$dep" >>"$tmp"
|
||||
fi
|
||||
mv "$tmp" "$workspace_file"
|
||||
fi
|
||||
|
||||
if [[ -f "$package_file" ]]; then
|
||||
node - "$package_file" "$dep" <<'EOF'
|
||||
const fs = require("node:fs");
|
||||
|
||||
const [packageFile, dep] = process.argv.slice(2);
|
||||
const data = JSON.parse(fs.readFileSync(packageFile, "utf8"));
|
||||
const list = data.pnpm?.onlyBuiltDependencies;
|
||||
if (Array.isArray(list)) {
|
||||
if (!list.includes(dep)) {
|
||||
list.unshift(dep);
|
||||
fs.writeFileSync(packageFile, `${JSON.stringify(data, null, 2)}\n`);
|
||||
}
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (!data.pnpm || typeof data.pnpm !== "object") {
|
||||
data.pnpm = {};
|
||||
}
|
||||
data.pnpm.onlyBuiltDependencies = [dep];
|
||||
fs.writeFileSync(packageFile, `${JSON.stringify(data, null, 2)}\n`);
|
||||
EOF
|
||||
fi
|
||||
|
||||
ui_info "Updated pnpm allowlist for git-hosted build dependency: ${dep}"
|
||||
}
|
||||
|
||||
# Install OpenClaw
|
||||
resolve_beta_version() {
|
||||
local beta=""
|
||||
@ -2113,7 +2395,12 @@ maybe_open_dashboard() {
|
||||
}
|
||||
|
||||
resolve_workspace_dir() {
|
||||
local profile="${OPENCLAW_PROFILE:-default}"
|
||||
if [[ -n "${INSTALL_WORKSPACE}" ]]; then
|
||||
echo "${INSTALL_WORKSPACE}"
|
||||
return
|
||||
fi
|
||||
local profile=""
|
||||
profile="$(resolve_install_profile)"
|
||||
if [[ "${profile}" != "default" ]]; then
|
||||
echo "${HOME}/.openclaw/workspace-${profile}"
|
||||
else
|
||||
@ -2121,13 +2408,133 @@ resolve_workspace_dir() {
|
||||
fi
|
||||
}
|
||||
|
||||
resolve_install_profile() {
|
||||
local profile="${INSTALL_PROFILE:-${OPENCLAW_PROFILE:-default}}"
|
||||
local profile_lc=""
|
||||
profile_lc="$(printf '%s' "${profile}" | tr '[:upper:]' '[:lower:]')"
|
||||
if [[ -z "${profile}" || "${profile_lc}" == "default" ]]; then
|
||||
echo "default"
|
||||
return
|
||||
fi
|
||||
echo "${profile}"
|
||||
}
|
||||
|
||||
resolve_install_state_dir() {
|
||||
if [[ -n "${OPENCLAW_STATE_DIR:-}" ]]; then
|
||||
echo "${OPENCLAW_STATE_DIR}"
|
||||
return
|
||||
fi
|
||||
local profile=""
|
||||
profile="$(resolve_install_profile)"
|
||||
if [[ "${profile}" != "default" ]]; then
|
||||
echo "${HOME}/.openclaw-${profile}"
|
||||
else
|
||||
echo "${HOME}/.openclaw"
|
||||
fi
|
||||
}
|
||||
|
||||
resolve_install_config_path() {
|
||||
if [[ -n "${OPENCLAW_CONFIG_PATH:-}" ]]; then
|
||||
echo "${OPENCLAW_CONFIG_PATH}"
|
||||
return
|
||||
fi
|
||||
local state_dir=""
|
||||
state_dir="$(resolve_install_state_dir)"
|
||||
echo "${state_dir}/openclaw.json"
|
||||
}
|
||||
|
||||
install_config_already_exists() {
|
||||
local profile=""
|
||||
local config_path=""
|
||||
profile="$(resolve_install_profile)"
|
||||
config_path="$(resolve_install_config_path)"
|
||||
if [[ -f "${config_path}" ]]; then
|
||||
return 0
|
||||
fi
|
||||
if [[ "${profile}" != "default" ]]; then
|
||||
return 1
|
||||
fi
|
||||
[[ -f "$HOME/.clawdbot/clawdbot.json" || -f "$HOME/.moltbot/moltbot.json" || -f "$HOME/.moldbot/moldbot.json" ]]
|
||||
}
|
||||
|
||||
validate_install_overrides() {
|
||||
if [[ -n "${INSTALL_PROFILE}" ]]; then
|
||||
if [[ ! "${INSTALL_PROFILE}" =~ ^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$ ]]; then
|
||||
ui_error "Invalid --profile: ${INSTALL_PROFILE} (use letters, numbers, '_' and '-' only)"
|
||||
exit 2
|
||||
fi
|
||||
export OPENCLAW_PROFILE="${INSTALL_PROFILE}"
|
||||
fi
|
||||
if [[ -n "${INSTALL_GATEWAY_PORT}" && ! "${INSTALL_GATEWAY_PORT}" =~ ^[0-9]+$ ]]; then
|
||||
ui_error "Invalid --gateway-port: ${INSTALL_GATEWAY_PORT}"
|
||||
exit 2
|
||||
fi
|
||||
}
|
||||
|
||||
resolve_supplied_install_profile() {
|
||||
local raw_profile=""
|
||||
local resolved_profile=""
|
||||
raw_profile="${INSTALL_PROFILE:-${OPENCLAW_PROFILE:-}}"
|
||||
if [[ -z "${raw_profile}" ]]; then
|
||||
echo ""
|
||||
return
|
||||
fi
|
||||
resolved_profile="$(resolve_install_profile)"
|
||||
if [[ "${resolved_profile}" == "default" ]]; then
|
||||
echo ""
|
||||
return
|
||||
fi
|
||||
echo "${resolved_profile}"
|
||||
}
|
||||
|
||||
build_onboard_command() {
|
||||
local claw="$1"
|
||||
local selected_profile=""
|
||||
selected_profile="$(resolve_supplied_install_profile)"
|
||||
ONBOARD_CMD=("$claw")
|
||||
if [[ -n "${selected_profile}" ]]; then
|
||||
ONBOARD_CMD+=("--profile" "${selected_profile}")
|
||||
fi
|
||||
ONBOARD_CMD+=("onboard")
|
||||
if [[ -n "${INSTALL_WORKSPACE}" ]]; then
|
||||
ONBOARD_CMD+=("--workspace" "${INSTALL_WORKSPACE}")
|
||||
fi
|
||||
if [[ -n "${INSTALL_GATEWAY_PORT}" ]]; then
|
||||
ONBOARD_CMD+=("--gateway-port" "${INSTALL_GATEWAY_PORT}")
|
||||
fi
|
||||
}
|
||||
|
||||
build_onboard_display_command() {
|
||||
local claw_name="${1:-openclaw}"
|
||||
local selected_profile=""
|
||||
selected_profile="$(resolve_supplied_install_profile)"
|
||||
ONBOARD_DISPLAY_CMD=("$claw_name")
|
||||
if [[ -n "${selected_profile}" ]]; then
|
||||
ONBOARD_DISPLAY_CMD+=("--profile" "${selected_profile}")
|
||||
fi
|
||||
ONBOARD_DISPLAY_CMD+=("onboard")
|
||||
if [[ -n "${INSTALL_WORKSPACE}" ]]; then
|
||||
ONBOARD_DISPLAY_CMD+=("--workspace" "${INSTALL_WORKSPACE}")
|
||||
fi
|
||||
if [[ -n "${INSTALL_GATEWAY_PORT}" ]]; then
|
||||
ONBOARD_DISPLAY_CMD+=("--gateway-port" "${INSTALL_GATEWAY_PORT}")
|
||||
fi
|
||||
}
|
||||
|
||||
format_onboard_display_command() {
|
||||
local claw_name="${1:-openclaw}"
|
||||
build_onboard_display_command "$claw_name"
|
||||
local formatted=""
|
||||
printf -v formatted '%q ' "${ONBOARD_DISPLAY_CMD[@]}"
|
||||
echo "${formatted% }"
|
||||
}
|
||||
|
||||
run_bootstrap_onboarding_if_needed() {
|
||||
if [[ "${NO_ONBOARD}" == "1" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
local config_path="${OPENCLAW_CONFIG_PATH:-$HOME/.openclaw/openclaw.json}"
|
||||
if [[ -f "${config_path}" || -f "$HOME/.clawdbot/clawdbot.json" || -f "$HOME/.moltbot/moltbot.json" || -f "$HOME/.moldbot/moldbot.json" ]]; then
|
||||
if install_config_already_exists; then
|
||||
return
|
||||
fi
|
||||
|
||||
@ -2140,7 +2547,9 @@ run_bootstrap_onboarding_if_needed() {
|
||||
fi
|
||||
|
||||
if [[ ! -r /dev/tty || ! -w /dev/tty ]]; then
|
||||
ui_info "BOOTSTRAP.md found but no TTY; run openclaw onboard to finish setup"
|
||||
local onboard_cmd=""
|
||||
onboard_cmd="$(format_onboard_display_command)"
|
||||
ui_info "BOOTSTRAP.md found but no TTY; run ${onboard_cmd} to finish setup"
|
||||
return
|
||||
fi
|
||||
|
||||
@ -2155,8 +2564,12 @@ run_bootstrap_onboarding_if_needed() {
|
||||
return
|
||||
fi
|
||||
|
||||
"$claw" onboard || {
|
||||
ui_error "Onboarding failed; run openclaw onboard to retry"
|
||||
build_onboard_command "$claw"
|
||||
"${ONBOARD_CMD[@]}" || {
|
||||
local onboard_cmd=""
|
||||
onboard_cmd="$(format_onboard_display_command "$(basename "$claw")")"
|
||||
ui_error "Onboarding failed; run ${onboard_cmd} to retry"
|
||||
ui_info "If gateway startup looks unhealthy, run: openclaw gateway status --deep"
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -2233,7 +2646,7 @@ refresh_gateway_service_if_loaded() {
|
||||
return 0
|
||||
fi
|
||||
|
||||
run_quiet_step "Probing gateway service" "$claw" gateway status --probe --deep || true
|
||||
run_quiet_step "Probing gateway service" "$claw" gateway status --deep || true
|
||||
}
|
||||
|
||||
# Main installation flow
|
||||
@ -2243,6 +2656,8 @@ main() {
|
||||
return 0
|
||||
fi
|
||||
|
||||
validate_install_overrides
|
||||
|
||||
bootstrap_gum_temp || true
|
||||
print_installer_banner
|
||||
print_gum_status
|
||||
@ -2355,6 +2770,7 @@ main() {
|
||||
ui_stage "Finalizing setup"
|
||||
|
||||
OPENCLAW_BIN="$(resolve_openclaw_bin || true)"
|
||||
warn_duplicate_openclaw_global_installs || true
|
||||
|
||||
# PATH warning: installs can succeed while the user's login shell still lacks npm's global bin dir.
|
||||
local npm_bin=""
|
||||
@ -2441,7 +2857,7 @@ main() {
|
||||
ui_section "Source install details"
|
||||
ui_kv "Checkout" "$final_git_dir"
|
||||
ui_kv "Wrapper" "$HOME/.local/bin/openclaw"
|
||||
ui_kv "Update command" "openclaw update --restart"
|
||||
ui_kv "Update command" "openclaw update"
|
||||
ui_kv "Switch to npm" "curl -fsSL --proto '=https' --tlsv1.2 https://openclaw.ai/install.sh | bash -s -- --install-method npm"
|
||||
elif [[ "$is_upgrade" == "true" ]]; then
|
||||
ui_info "Upgrade complete"
|
||||
@ -2456,15 +2872,13 @@ main() {
|
||||
return 0
|
||||
fi
|
||||
local -a doctor_args=()
|
||||
if [[ "$NO_ONBOARD" == "1" ]]; then
|
||||
if "$claw" doctor --help 2>/dev/null | grep -q -- "--non-interactive"; then
|
||||
doctor_args+=("--non-interactive")
|
||||
fi
|
||||
if [[ "$NO_ONBOARD" == "1" || "$NO_PROMPT" == "1" ]]; then
|
||||
doctor_args+=("--non-interactive")
|
||||
fi
|
||||
ui_info "Running openclaw doctor"
|
||||
local doctor_ok=0
|
||||
if (( ${#doctor_args[@]} )); then
|
||||
OPENCLAW_UPDATE_IN_PROGRESS=1 "$claw" doctor "${doctor_args[@]}" </dev/tty && doctor_ok=1
|
||||
OPENCLAW_UPDATE_IN_PROGRESS=1 "$claw" doctor "${doctor_args[@]}" </dev/null && doctor_ok=1
|
||||
else
|
||||
OPENCLAW_UPDATE_IN_PROGRESS=1 "$claw" doctor </dev/tty && doctor_ok=1
|
||||
fi
|
||||
@ -2479,10 +2893,11 @@ main() {
|
||||
fi
|
||||
else
|
||||
if [[ "$NO_ONBOARD" == "1" || "$skip_onboard" == "true" ]]; then
|
||||
ui_info "Skipping onboard (requested); run openclaw onboard later"
|
||||
local onboard_cmd=""
|
||||
onboard_cmd="$(format_onboard_display_command)"
|
||||
ui_info "Skipping onboard (requested); run ${onboard_cmd} later"
|
||||
else
|
||||
local config_path="${OPENCLAW_CONFIG_PATH:-$HOME/.openclaw/openclaw.json}"
|
||||
if [[ -f "${config_path}" || -f "$HOME/.clawdbot/clawdbot.json" || -f "$HOME/.moltbot/moltbot.json" || -f "$HOME/.moldbot/moldbot.json" ]]; then
|
||||
if install_config_already_exists; then
|
||||
ui_info "Config already present; running doctor"
|
||||
run_doctor
|
||||
should_open_dashboard=true
|
||||
@ -2502,9 +2917,12 @@ main() {
|
||||
return 0
|
||||
fi
|
||||
exec </dev/tty
|
||||
exec "$claw" onboard
|
||||
build_onboard_command "$claw"
|
||||
exec "${ONBOARD_CMD[@]}"
|
||||
fi
|
||||
ui_info "No TTY; run openclaw onboard to finish setup"
|
||||
local onboard_cmd=""
|
||||
onboard_cmd="$(format_onboard_display_command)"
|
||||
ui_info "No TTY; run ${onboard_cmd} to finish setup"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
3
public/sponsors/github-light.svg
Normal file
3
public/sponsors/github-light.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#111827" aria-hidden="true">
|
||||
<path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 829 B |
3
public/sponsors/github.svg
Normal file
3
public/sponsors/github.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#fff" aria-hidden="true">
|
||||
<path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 826 B |
9
public/sponsors/nvidia-dark.svg
Normal file
9
public/sponsors/nvidia-dark.svg
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 164 30" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<g>
|
||||
<path d="M160.352,24.069L160.352,23.62L160.64,23.62C160.797,23.62 161.011,23.632 161.011,23.824C161.011,24.032 160.901,24.069 160.715,24.069L160.352,24.069M160.352,24.384L160.544,24.384L160.991,25.168L161.481,25.168L160.987,24.352C161.242,24.333 161.452,24.212 161.452,23.868C161.452,23.441 161.157,23.303 160.659,23.303L159.938,23.303L159.938,25.168L160.352,25.168L160.352,24.384M162.45,24.238C162.45,23.143 161.599,22.508 160.65,22.508C159.695,22.508 158.845,23.143 158.845,24.238C158.845,25.333 159.695,25.971 160.65,25.971C161.598,25.971 162.45,25.333 162.45,24.238M161.93,24.238C161.93,25.036 161.343,25.572 160.65,25.572L160.65,25.566C159.937,25.572 159.361,25.036 159.361,24.238C159.361,23.441 159.938,22.907 160.65,22.907C161.344,22.907 161.93,23.441 161.93,24.238" style="fill:white;"/>
|
||||
<path d="M96.374,5.707L96.376,25.367L101.928,25.367L101.928,5.707L96.374,5.707ZM52.697,5.681L52.697,25.367L58.3,25.367L58.3,10.086L62.67,10.1C64.107,10.1 65.1,10.445 65.793,11.184C66.672,12.12 67.03,13.628 67.03,16.389L67.03,25.367L72.457,25.367L72.457,14.49C72.457,6.727 67.509,5.68 62.668,5.68L52.698,5.68L52.697,5.681ZM105.314,5.708L105.314,25.367L114.32,25.367C119.118,25.367 120.684,24.569 122.377,22.78C123.575,21.524 124.348,18.766 124.348,15.753C124.348,12.99 123.693,10.525 122.551,8.99C120.494,6.245 117.531,5.708 113.106,5.708L105.314,5.708ZM110.822,9.988L113.209,9.988C116.672,9.988 118.912,11.544 118.912,15.579C118.912,19.616 116.672,21.171 113.209,21.171L110.822,21.171L110.822,9.988ZM88.369,5.708L83.735,21.288L79.295,5.709L73.302,5.708L79.642,25.367L87.645,25.367L94.036,5.708L88.369,5.708ZM126.932,25.367L132.485,25.367L132.485,5.709L126.93,5.708L126.932,25.367ZM142.496,5.715L134.743,25.36L140.218,25.36L141.445,21.888L150.62,21.888L151.781,25.36L157.725,25.36L149.913,5.714L142.496,5.715ZM146.1,9.3L149.464,18.504L142.631,18.504L146.101,9.3L146.1,9.3Z" style="fill:white;"/>
|
||||
<path d="M16.889,8.985L16.889,6.28C17.151,6.26 17.417,6.247 17.687,6.238C25.087,6.006 29.942,12.597 29.942,12.597C29.942,12.597 24.698,19.879 19.076,19.879C18.333,19.882 17.594,19.764 16.889,19.529L16.889,11.325C19.769,11.673 20.349,12.945 22.081,15.833L25.933,12.585C25.933,12.585 23.121,8.897 18.381,8.897C17.866,8.897 17.373,8.933 16.889,8.985ZM16.889,0.047L16.889,4.09C17.154,4.069 17.42,4.052 17.687,4.042C27.977,3.696 34.682,12.482 34.682,12.482C34.682,12.482 26.982,21.846 18.959,21.846C18.224,21.846 17.535,21.778 16.889,21.663L16.889,24.161C17.442,24.231 18.015,24.273 18.613,24.273C26.078,24.273 31.477,20.461 36.705,15.948C37.572,16.642 41.121,18.331 41.85,19.071C36.879,23.231 25.295,26.586 18.727,26.586C18.113,26.584 17.5,26.552 16.889,26.49L16.889,30L45.264,30L45.264,0.047L16.889,0.047ZM16.889,19.529L16.889,21.662C9.984,20.432 8.067,13.254 8.067,13.254C8.067,13.254 11.383,9.58 16.889,8.985L16.889,11.325L16.878,11.324C13.988,10.977 11.731,13.677 11.731,13.677C11.731,13.677 12.996,18.221 16.889,19.529ZM4.625,12.943C4.625,12.943 8.717,6.903 16.889,6.28L16.889,4.088C7.838,4.815 0,12.48 0,12.48C0,12.48 4.439,25.313 16.889,26.488L16.889,24.16C7.753,23.011 4.625,12.943 4.625,12.943Z" style="fill:rgb(118,185,0);"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
9
public/sponsors/nvidia-light.svg
Normal file
9
public/sponsors/nvidia-light.svg
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 164 30" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<g>
|
||||
<path d="M160.352,24.069L160.352,23.62L160.64,23.62C160.797,23.62 161.011,23.632 161.011,23.824C161.011,24.032 160.901,24.069 160.715,24.069L160.352,24.069M160.352,24.384L160.544,24.384L160.991,25.168L161.481,25.168L160.987,24.352C161.242,24.333 161.452,24.212 161.452,23.868C161.452,23.441 161.157,23.303 160.659,23.303L159.938,23.303L159.938,25.168L160.352,25.168L160.352,24.384M162.45,24.238C162.45,23.143 161.599,22.508 160.65,22.508C159.695,22.508 158.845,23.143 158.845,24.238C158.845,25.333 159.695,25.971 160.65,25.971C161.598,25.971 162.45,25.333 162.45,24.238M161.93,24.238C161.93,25.036 161.343,25.572 160.65,25.572L160.65,25.566C159.937,25.572 159.361,25.036 159.361,24.238C159.361,23.441 159.938,22.907 160.65,22.907C161.344,22.907 161.93,23.441 161.93,24.238" style="fill:black;"/>
|
||||
<path d="M96.374,5.707L96.376,25.367L101.928,25.367L101.928,5.707L96.374,5.707ZM52.697,5.681L52.697,25.367L58.3,25.367L58.3,10.086L62.67,10.1C64.107,10.1 65.1,10.445 65.793,11.184C66.672,12.12 67.03,13.628 67.03,16.389L67.03,25.367L72.457,25.367L72.457,14.49C72.457,6.727 67.509,5.68 62.668,5.68L52.698,5.68L52.697,5.681ZM105.314,5.708L105.314,25.367L114.32,25.367C119.118,25.367 120.684,24.569 122.377,22.78C123.575,21.524 124.348,18.766 124.348,15.753C124.348,12.99 123.693,10.525 122.551,8.99C120.494,6.245 117.531,5.708 113.106,5.708L105.314,5.708ZM110.822,9.988L113.209,9.988C116.672,9.988 118.912,11.544 118.912,15.579C118.912,19.616 116.672,21.171 113.209,21.171L110.822,21.171L110.822,9.988ZM88.369,5.708L83.735,21.288L79.295,5.709L73.302,5.708L79.642,25.367L87.645,25.367L94.036,5.708L88.369,5.708ZM126.932,25.367L132.485,25.367L132.485,5.709L126.93,5.708L126.932,25.367ZM142.496,5.715L134.743,25.36L140.218,25.36L141.445,21.888L150.62,21.888L151.781,25.36L157.725,25.36L149.913,5.714L142.496,5.715ZM146.1,9.3L149.464,18.504L142.631,18.504L146.101,9.3L146.1,9.3Z" style="fill:black;"/>
|
||||
<path d="M16.889,8.985L16.889,6.28C17.151,6.26 17.417,6.247 17.687,6.238C25.087,6.006 29.942,12.597 29.942,12.597C29.942,12.597 24.698,19.879 19.076,19.879C18.333,19.882 17.594,19.764 16.889,19.529L16.889,11.325C19.769,11.673 20.349,12.945 22.081,15.833L25.933,12.585C25.933,12.585 23.121,8.897 18.381,8.897C17.866,8.897 17.373,8.933 16.889,8.985ZM16.889,0.047L16.889,4.09C17.154,4.069 17.42,4.052 17.687,4.042C27.977,3.696 34.682,12.482 34.682,12.482C34.682,12.482 26.982,21.846 18.959,21.846C18.224,21.846 17.535,21.778 16.889,21.663L16.889,24.161C17.442,24.231 18.015,24.273 18.613,24.273C26.078,24.273 31.477,20.461 36.705,15.948C37.572,16.642 41.121,18.331 41.85,19.071C36.879,23.231 25.295,26.586 18.727,26.586C18.113,26.584 17.5,26.552 16.889,26.49L16.889,30L45.264,30L45.264,0.047L16.889,0.047ZM16.889,19.529L16.889,21.662C9.984,20.432 8.067,13.254 8.067,13.254C8.067,13.254 11.383,9.58 16.889,8.985L16.889,11.325L16.878,11.324C13.988,10.977 11.731,13.677 11.731,13.677C11.731,13.677 12.996,18.221 16.889,19.529ZM4.625,12.943C4.625,12.943 8.717,6.903 16.889,6.28L16.889,4.088C7.838,4.815 0,12.48 0,12.48C0,12.48 4.439,25.313 16.889,26.488L16.889,24.16C7.753,23.011 4.625,12.943 4.625,12.943Z" style="fill:rgb(118,185,0);"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
@ -18,6 +18,15 @@ curl_cli_install() {
|
||||
fi
|
||||
}
|
||||
|
||||
extract_version() {
|
||||
local raw="$1"
|
||||
if [[ "$raw" =~ ([0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z]+)*) ]]; then
|
||||
printf '%s\n' "${BASH_REMATCH[1]}"
|
||||
else
|
||||
printf '%s\n' "$raw"
|
||||
fi
|
||||
}
|
||||
|
||||
echo "==> CLI installer: --help"
|
||||
curl_cli_install | bash -s -- --help >/tmp/install-cli-help.txt
|
||||
grep -q -- "--install-method" /tmp/install-cli-help.txt
|
||||
@ -53,8 +62,9 @@ echo "==> Verify openclaw runs"
|
||||
|
||||
echo "==> Verify version matches checkout"
|
||||
EXPECTED_VERSION="$(node -e "console.log(JSON.parse(require('fs').readFileSync('${REPO_DIR}/package.json','utf8')).version)")"
|
||||
INSTALLED_VERSION="$("${INSTALL_PREFIX}/bin/openclaw" --version 2>/dev/null | head -n 1 | tr -d '\r')"
|
||||
echo "installed=$INSTALLED_VERSION expected=$EXPECTED_VERSION"
|
||||
INSTALLED_VERSION_RAW="$("${INSTALL_PREFIX}/bin/openclaw" --version 2>/dev/null | head -n 1 | tr -d '\r')"
|
||||
INSTALLED_VERSION="$(extract_version "$INSTALLED_VERSION_RAW")"
|
||||
echo "installed=$INSTALLED_VERSION raw=$INSTALLED_VERSION_RAW expected=$EXPECTED_VERSION"
|
||||
if [[ "$INSTALLED_VERSION" != "$EXPECTED_VERSION" ]]; then
|
||||
echo "ERROR: expected openclaw@$EXPECTED_VERSION, got $INSTALLED_VERSION" >&2
|
||||
exit 1
|
||||
|
||||
@ -20,6 +20,15 @@ curl_install() {
|
||||
fi
|
||||
}
|
||||
|
||||
extract_version() {
|
||||
local raw="$1"
|
||||
if [[ "$raw" =~ ([0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z]+)*) ]]; then
|
||||
printf '%s\n' "${BASH_REMATCH[1]}"
|
||||
else
|
||||
printf '%s\n' "$raw"
|
||||
fi
|
||||
}
|
||||
|
||||
echo "==> Installer: --help"
|
||||
curl_install | bash -s -- --help >/tmp/install-help.txt
|
||||
grep -q -- "--install-method" /tmp/install-help.txt
|
||||
@ -63,8 +72,9 @@ openclaw --help >/dev/null
|
||||
|
||||
echo "==> Verify version matches checkout"
|
||||
EXPECTED_VERSION="$(node -e "console.log(JSON.parse(require('fs').readFileSync('${REPO_DIR}/package.json','utf8')).version)")"
|
||||
INSTALLED_VERSION="$(openclaw --version 2>/dev/null | head -n 1 | tr -d '\r')"
|
||||
echo "installed=$INSTALLED_VERSION expected=$EXPECTED_VERSION"
|
||||
INSTALLED_VERSION_RAW="$(openclaw --version 2>/dev/null | head -n 1 | tr -d '\r')"
|
||||
INSTALLED_VERSION="$(extract_version "$INSTALLED_VERSION_RAW")"
|
||||
echo "installed=$INSTALLED_VERSION raw=$INSTALLED_VERSION_RAW expected=$EXPECTED_VERSION"
|
||||
if [[ "$INSTALLED_VERSION" != "$EXPECTED_VERSION" ]]; then
|
||||
echo "ERROR: expected openclaw@$EXPECTED_VERSION, got $INSTALLED_VERSION" >&2
|
||||
exit 1
|
||||
|
||||
@ -35,6 +35,15 @@ curl_cli_install() {
|
||||
fi
|
||||
}
|
||||
|
||||
extract_version() {
|
||||
local raw="$1"
|
||||
if [[ "$raw" =~ ([0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z]+)*) ]]; then
|
||||
printf '%s\n' "${BASH_REMATCH[1]}"
|
||||
else
|
||||
printf '%s\n' "$raw"
|
||||
fi
|
||||
}
|
||||
|
||||
echo "==> Installer: --help"
|
||||
curl_install | bash -s -- --help >/tmp/install-help.txt
|
||||
grep -q -- "--install-method" /tmp/install-help.txt
|
||||
@ -100,9 +109,10 @@ if [[ -z "$NEXT_VERSION" ]]; then
|
||||
NEXT_VERSION="$LATEST_VERSION"
|
||||
fi
|
||||
|
||||
INSTALLED_VERSION="$("$CMD_PATH" --version 2>/dev/null | head -n 1 | tr -d '\r')"
|
||||
INSTALLED_VERSION_RAW="$("$CMD_PATH" --version 2>/dev/null | head -n 1 | tr -d '\r')"
|
||||
INSTALLED_VERSION="$(extract_version "$INSTALLED_VERSION_RAW")"
|
||||
|
||||
echo "installed=$INSTALLED_VERSION latest=$LATEST_VERSION next=$NEXT_VERSION"
|
||||
echo "installed=$INSTALLED_VERSION raw=$INSTALLED_VERSION_RAW latest=$LATEST_VERSION next=$NEXT_VERSION"
|
||||
if [[ "$INSTALLED_VERSION" != "$LATEST_VERSION" && "$INSTALLED_VERSION" != "$NEXT_VERSION" ]]; then
|
||||
echo "ERROR: expected ${PKG_NAME}@$LATEST_VERSION (latest) or @$NEXT_VERSION (next), got @$INSTALLED_VERSION" >&2
|
||||
exit 1
|
||||
|
||||
@ -13,5 +13,9 @@ docker run --rm -t "$IMAGE_NAME" bash -lc '
|
||||
install_prefix="/tmp/clawdbot"
|
||||
/app/public/install-cli.sh --json --no-onboard --prefix "$install_prefix" > /tmp/install.jsonl
|
||||
grep "\"event\":\"done\"" /tmp/install.jsonl
|
||||
"$install_prefix/bin/clawdbot" --version
|
||||
if [[ -x "$install_prefix/bin/openclaw" ]]; then
|
||||
"$install_prefix/bin/openclaw" --version
|
||||
else
|
||||
"$install_prefix/bin/clawdbot" --version
|
||||
fi
|
||||
'
|
||||
|
||||
250
scripts/test-install-cli-unit.sh
Normal file
250
scripts/test-install-cli-unit.sh
Normal file
@ -0,0 +1,250 @@
|
||||
#!/usr/bin/env bash
|
||||
# shellcheck disable=SC1091,SC2030,SC2031,SC2016,SC2329,SC2317
|
||||
set -euo pipefail
|
||||
|
||||
fail() {
|
||||
echo "FAIL: $*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
assert_eq() {
|
||||
local got="$1"
|
||||
local want="$2"
|
||||
local msg="${3:-}"
|
||||
if [[ "$got" != "$want" ]]; then
|
||||
fail "${msg} expected=${want} got=${got}"
|
||||
fi
|
||||
}
|
||||
|
||||
assert_nonempty() {
|
||||
local got="$1"
|
||||
local msg="${2:-}"
|
||||
if [[ -z "$got" ]]; then
|
||||
fail "${msg} expected non-empty"
|
||||
fi
|
||||
}
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
TMP_DIR="$(mktemp -d)"
|
||||
trap 'rm -rf "$TMP_DIR"' EXIT
|
||||
|
||||
export OPENCLAW_INSTALL_CLI_SH_NO_RUN=1
|
||||
# shellcheck source=../public/install-cli.sh
|
||||
source "${ROOT_DIR}/public/install-cli.sh"
|
||||
|
||||
echo "==> case: ensure_home_env repairs root HOME"
|
||||
(
|
||||
root="${TMP_DIR}/case-home-env"
|
||||
home_dir="${root}/home"
|
||||
tool_bin="${root}/tool-bin"
|
||||
mkdir -p "${home_dir}" "${tool_bin}"
|
||||
|
||||
cat >"${tool_bin}/id" <<EOF
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
if [[ "\${1:-}" == "-un" ]]; then
|
||||
echo vmroot
|
||||
exit 0
|
||||
fi
|
||||
exit 1
|
||||
EOF
|
||||
chmod +x "${tool_bin}/id"
|
||||
|
||||
cat >"${tool_bin}/getent" <<EOF
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
if [[ "\${1:-}" == "passwd" && "\${2:-}" == "vmroot" ]]; then
|
||||
echo 'vmroot:x:0:0:VM Root:${home_dir}:/bin/bash'
|
||||
exit 0
|
||||
fi
|
||||
exit 1
|
||||
EOF
|
||||
chmod +x "${tool_bin}/getent"
|
||||
|
||||
export PATH="${tool_bin}:/usr/bin:/bin"
|
||||
export HOME="/"
|
||||
|
||||
ensure_home_env
|
||||
assert_eq "$HOME" "${home_dir}" "ensure_home_env root HOME"
|
||||
)
|
||||
|
||||
echo "==> case: ensure_pnpm_binary_for_scripts installs prefix wrapper"
|
||||
(
|
||||
root="${TMP_DIR}/case-pnpm-wrapper"
|
||||
prefix="${root}/prefix"
|
||||
fake_node="${root}/node/bin"
|
||||
mkdir -p "${prefix}" "${fake_node}"
|
||||
|
||||
cat >"${fake_node}/corepack" <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
if [[ "${1:-}" == "pnpm" && "${2:-}" == "--version" ]]; then
|
||||
echo "10.29.2"
|
||||
exit 0
|
||||
fi
|
||||
if [[ "${1:-}" == "pnpm" ]]; then
|
||||
shift
|
||||
exec "${BASH_SOURCE%/*}/pnpm-real" "$@"
|
||||
fi
|
||||
exit 1
|
||||
EOF
|
||||
chmod +x "${fake_node}/corepack"
|
||||
|
||||
cat >"${fake_node}/pnpm-real" <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
if [[ "${1:-}" == "--version" ]]; then
|
||||
echo "10.29.2"
|
||||
exit 0
|
||||
fi
|
||||
echo "wrapped:$*"
|
||||
EOF
|
||||
chmod +x "${fake_node}/pnpm-real"
|
||||
|
||||
export PREFIX="${prefix}"
|
||||
export PATH="/usr/bin:/bin"
|
||||
set_pnpm_cmd "${fake_node}/corepack" pnpm
|
||||
|
||||
ensure_pnpm_binary_for_scripts
|
||||
|
||||
got="$(command -v pnpm || true)"
|
||||
assert_eq "$got" "${prefix}/bin/pnpm" "ensure_pnpm_binary_for_scripts wrapper path"
|
||||
out="$(pnpm --version)"
|
||||
assert_eq "$out" "10.29.2" "ensure_pnpm_binary_for_scripts wrapper version"
|
||||
)
|
||||
|
||||
echo "==> case: ensure_pnpm_git_prepare_allowlist appends known dep"
|
||||
(
|
||||
root="${TMP_DIR}/case-allowlist"
|
||||
repo="${root}/repo"
|
||||
prefix="${root}/prefix"
|
||||
fake_node_dir="${prefix}/tools/node-v${NODE_VERSION}/bin"
|
||||
mkdir -p "${repo}" "${fake_node_dir}"
|
||||
cat >"${repo}/pnpm-workspace.yaml" <<'EOF'
|
||||
packages:
|
||||
- .
|
||||
|
||||
onlyBuiltDependencies:
|
||||
- esbuild
|
||||
EOF
|
||||
cat >"${repo}/package.json" <<'EOF'
|
||||
{
|
||||
"name": "repo",
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
"esbuild"
|
||||
]
|
||||
}
|
||||
}
|
||||
EOF
|
||||
cat >"${fake_node_dir}/node" <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
python3 - "$@" <<'PY'
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
package_file = pathlib.Path(sys.argv[2])
|
||||
dep = sys.argv[3]
|
||||
data = json.loads(package_file.read_text())
|
||||
pnpm = data.setdefault("pnpm", {})
|
||||
deps = pnpm.setdefault("onlyBuiltDependencies", [])
|
||||
if dep not in deps:
|
||||
deps.insert(0, dep)
|
||||
package_file.write_text(json.dumps(data, indent=2) + "\n")
|
||||
PY
|
||||
EOF
|
||||
chmod +x "${fake_node_dir}/node"
|
||||
|
||||
export PREFIX="${prefix}"
|
||||
ensure_pnpm_git_prepare_allowlist "${repo}"
|
||||
ensure_pnpm_git_prepare_allowlist "${repo}"
|
||||
|
||||
workspace_count="$(grep -c '@tloncorp/api' "${repo}/pnpm-workspace.yaml" || true)"
|
||||
package_count="$(grep -c '@tloncorp/api' "${repo}/package.json" || true)"
|
||||
assert_eq "$workspace_count" "1" "ensure_pnpm_git_prepare_allowlist workspace count"
|
||||
assert_eq "$package_count" "1" "ensure_pnpm_git_prepare_allowlist package count"
|
||||
)
|
||||
|
||||
echo "==> case: install_openclaw keeps a single package root under toolchain"
|
||||
(
|
||||
root="${TMP_DIR}/case-install-npm"
|
||||
prefix="${root}/prefix"
|
||||
fake_bin="${root}/bin"
|
||||
fake_npm="${fake_bin}/npm"
|
||||
toolchain_dir="${prefix}/tools/node-v${NODE_VERSION}"
|
||||
entry_js="${toolchain_dir}/lib/node_modules/openclaw/dist/entry.js"
|
||||
mkdir -p "${fake_bin}" "${toolchain_dir}/bin" "$(dirname "${entry_js}")"
|
||||
printf 'console.log("ok")\n' > "${entry_js}"
|
||||
printf 'legacy-bin\n' > "${toolchain_dir}/bin/openclaw"
|
||||
|
||||
cat >"${fake_npm}" <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
printf '%s\n' "$*" > "${FAKE_NPM_ARGS_FILE}"
|
||||
EOF
|
||||
chmod +x "${fake_npm}"
|
||||
|
||||
export PREFIX="${prefix}"
|
||||
export FAKE_NPM_ARGS_FILE="${root}/npm-args.txt"
|
||||
export SHARP_IGNORE_GLOBAL_LIBVIPS=1
|
||||
export OPENCLAW_VERSION=latest
|
||||
export NPM_LOGLEVEL=error
|
||||
|
||||
npm_bin() { echo "${fake_npm}"; }
|
||||
log() { :; }
|
||||
emit_json() { :; }
|
||||
fix_npm_prefix_if_needed() { :; }
|
||||
|
||||
install_openclaw
|
||||
|
||||
args="$(cat "${FAKE_NPM_ARGS_FILE}")"
|
||||
assert_eq "$args" "install -g --prefix ${toolchain_dir} --loglevel error --no-fund --no-audit openclaw@latest" "install_openclaw npm prefix"
|
||||
test -x "${prefix}/bin/openclaw"
|
||||
test -e "${toolchain_dir}/bin/openclaw"
|
||||
wrapper_target="$(python3 - <<'PY' "${prefix}/bin/openclaw"
|
||||
import pathlib
|
||||
import sys
|
||||
print(pathlib.Path(sys.argv[1]).read_text().splitlines()[-1])
|
||||
PY
|
||||
)"
|
||||
assert_eq "$wrapper_target" "exec \"${prefix}/tools/node/bin/node\" \"${toolchain_dir}/lib/node_modules/openclaw/dist/entry.js\" \"\$@\"" "install_openclaw wrapper target"
|
||||
)
|
||||
|
||||
echo "==> case: install_openclaw_from_git uses run_pnpm"
|
||||
(
|
||||
root="${TMP_DIR}/case-install-git"
|
||||
repo="${root}/repo"
|
||||
prefix="${root}/prefix"
|
||||
home_dir="${root}/home"
|
||||
mkdir -p "${repo}/.git" "${repo}/dist" "${prefix}" "${home_dir}"
|
||||
: > "${repo}/dist/entry.js"
|
||||
|
||||
export HOME="${home_dir}"
|
||||
export PREFIX="${prefix}"
|
||||
export GIT_UPDATE=0
|
||||
export SHARP_IGNORE_GLOBAL_LIBVIPS=1
|
||||
|
||||
run_pnpm_calls=()
|
||||
|
||||
ensure_git() { :; }
|
||||
ensure_pnpm() { set_pnpm_cmd echo pnpm; }
|
||||
ensure_pnpm_binary_for_scripts() { :; }
|
||||
cleanup_legacy_submodules() { :; }
|
||||
log() { :; }
|
||||
emit_json() { :; }
|
||||
fail() { echo "FAIL: $*" >&2; exit 1; }
|
||||
run_pnpm() {
|
||||
run_pnpm_calls+=("$*")
|
||||
return 0
|
||||
}
|
||||
|
||||
install_openclaw_from_git "${repo}"
|
||||
assert_eq "${run_pnpm_calls[0]}" "-C ${repo} install" "install_openclaw_from_git deps command"
|
||||
assert_eq "${run_pnpm_calls[1]}" "-C ${repo} ui:build" "install_openclaw_from_git ui build command"
|
||||
assert_eq "${run_pnpm_calls[2]}" "-C ${repo} build" "install_openclaw_from_git build command"
|
||||
test -x "${prefix}/bin/openclaw"
|
||||
)
|
||||
|
||||
echo "PASS"
|
||||
63
scripts/test-install-ps1-unit.sh
Executable file
63
scripts/test-install-ps1-unit.sh
Executable file
@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
SCRIPTS=("${ROOT_DIR}/public/install.ps1")
|
||||
if [[ -f "${ROOT_DIR}/dist/install.ps1" ]]; then
|
||||
SCRIPTS+=("${ROOT_DIR}/dist/install.ps1")
|
||||
fi
|
||||
|
||||
fail() {
|
||||
echo "FAIL: $*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
require_contains() {
|
||||
local script="$1"
|
||||
local pattern="$2"
|
||||
if ! grep -Fq "$pattern" "$script"; then
|
||||
fail "$(realpath --relative-to="$ROOT_DIR" "$script"): missing pattern: $pattern"
|
||||
fi
|
||||
}
|
||||
|
||||
for script in "${SCRIPTS[@]}"; do
|
||||
exit_lines="$(grep -nE '^[[:space:]]*exit\b' "$script" || true)"
|
||||
# shellcheck disable=SC2016
|
||||
if [[ "$exit_lines" != *'exit $script:InstallExitCode'* ]]; then
|
||||
fail "$(realpath --relative-to="$ROOT_DIR" "$script"): expected the only installer exit to live in Complete-Install"
|
||||
fi
|
||||
if [[ "$(printf '%s\n' "$exit_lines" | sed '/^$/d' | wc -l | tr -d ' ')" != "1" ]]; then
|
||||
printf '%s\n' "$exit_lines" >&2
|
||||
fail "$(realpath --relative-to="$ROOT_DIR" "$script"): unexpected extra exit usage"
|
||||
fi
|
||||
|
||||
main_body="$(awk '
|
||||
/^function Main \{/ { in_main = 1; next }
|
||||
/^\$mainResults = @\(Main\)/ { in_main = 0 }
|
||||
in_main { print }
|
||||
' "$script")"
|
||||
|
||||
if grep -E '^[[:space:]]*exit\b' <<<"$main_body" >/dev/null; then
|
||||
fail "$(realpath --relative-to="$ROOT_DIR" "$script"): Main must not call exit"
|
||||
fi
|
||||
|
||||
require_contains "$script" 'function Fail-Install {'
|
||||
require_contains "$script" 'function Complete-Install {'
|
||||
require_contains "$script" 'function Resolve-NpmOpenClawInstallSpec {'
|
||||
# shellcheck disable=SC2016
|
||||
require_contains "$script" 'install -g "$installSpec"'
|
||||
# shellcheck disable=SC2016
|
||||
require_contains "$script" 'return "$PackageName@$trimmedTag"'
|
||||
require_contains "$script" 'return (Fail-Install -Code 2)'
|
||||
require_contains "$script" 'return (Fail-Install)'
|
||||
# shellcheck disable=SC2016
|
||||
require_contains "$script" '$mainResults = @(Main)'
|
||||
# shellcheck disable=SC2016
|
||||
require_contains "$script" '$installSucceeded = $mainResults.Count -gt 0 -and $mainResults[-1] -eq $true'
|
||||
# shellcheck disable=SC2016
|
||||
require_contains "$script" 'Complete-Install -Succeeded:$installSucceeded'
|
||||
# shellcheck disable=SC2016
|
||||
require_contains "$script" 'throw "OpenClaw installation failed with exit code $($script:InstallExitCode)."'
|
||||
done
|
||||
|
||||
echo "install.ps1 unit checks passed"
|
||||
@ -66,6 +66,23 @@ export CLAWDBOT_INSTALL_SH_NO_RUN=1
|
||||
# shellcheck source=../public/install.sh
|
||||
source "${ROOT_DIR}/public/install.sh"
|
||||
|
||||
echo "==> case: ensure_home_env repairs root HOME"
|
||||
(
|
||||
root="${TMP_DIR}/case-home-env"
|
||||
home_dir="${root}/home"
|
||||
tool_bin="${root}/tool-bin"
|
||||
mkdir -p "${home_dir}" "${tool_bin}"
|
||||
|
||||
make_exe "${tool_bin}/id" "if [[ \"\${1:-}\" == \"-un\" ]]; then echo vmroot; exit 0; fi; exit 1"
|
||||
make_exe "${tool_bin}/getent" "if [[ \"\${1:-}\" == \"passwd\" && \"\${2:-}\" == \"vmroot\" ]]; then echo 'vmroot:x:0:0:VM Root:${home_dir}:/bin/bash'; exit 0; fi; exit 1"
|
||||
|
||||
export PATH="${tool_bin}:/usr/bin:/bin"
|
||||
export HOME="/"
|
||||
|
||||
ensure_home_env
|
||||
assert_eq "$HOME" "${home_dir}" "ensure_home_env root HOME"
|
||||
)
|
||||
|
||||
echo "==> case: resolve_openclaw_bin (direct PATH)"
|
||||
(
|
||||
bin="${TMP_DIR}/case-path/bin"
|
||||
@ -133,6 +150,41 @@ echo "==> case: warn_openclaw_not_found (smoke)"
|
||||
assert_nonempty "$out" "warn_openclaw_not_found output"
|
||||
)
|
||||
|
||||
echo "==> case: duplicate OpenClaw install warning (multiple npm roots)"
|
||||
(
|
||||
root="${TMP_DIR}/case-duplicate-openclaw"
|
||||
mkdir -p "${root}/brew/openclaw" "${root}/fnm/openclaw"
|
||||
printf '{"version":"2026.3.7"}\n' > "${root}/brew/openclaw/package.json"
|
||||
printf '{"version":"2026.3.1"}\n' > "${root}/fnm/openclaw/package.json"
|
||||
|
||||
collect_openclaw_npm_root_candidates() { printf '%s\n' "${root}/brew" "${root}/fnm"; }
|
||||
# shellcheck disable=SC2034
|
||||
OPENCLAW_BIN="${root}/fnm/.bin/openclaw"
|
||||
ui_warn() { echo "WARN: $*"; }
|
||||
|
||||
out="$(warn_duplicate_openclaw_global_installs 2>&1)"
|
||||
assert_contains "$out" "Multiple OpenClaw global installs detected" "duplicate OpenClaw warning header"
|
||||
assert_contains "$out" "2026.3.7" "duplicate OpenClaw warning first version"
|
||||
assert_contains "$out" "2026.3.1" "duplicate OpenClaw warning second version"
|
||||
assert_contains "$out" "${root}/brew/openclaw" "duplicate OpenClaw warning first path"
|
||||
assert_contains "$out" "${root}/fnm/openclaw" "duplicate OpenClaw warning second path"
|
||||
assert_contains "$out" "Active openclaw:" "duplicate OpenClaw warning active binary"
|
||||
assert_contains "$out" "npm uninstall -g openclaw" "duplicate OpenClaw warning cleanup hint"
|
||||
)
|
||||
|
||||
echo "==> case: duplicate OpenClaw install warning (single npm root is quiet)"
|
||||
(
|
||||
root="${TMP_DIR}/case-single-openclaw"
|
||||
mkdir -p "${root}/only/openclaw"
|
||||
printf '{"version":"2026.3.7"}\n' > "${root}/only/openclaw/package.json"
|
||||
|
||||
collect_openclaw_npm_root_candidates() { printf '%s\n' "${root}/only"; }
|
||||
ui_warn() { echo "WARN: $*"; }
|
||||
|
||||
out="$(warn_duplicate_openclaw_global_installs 2>&1 || true)"
|
||||
assert_eq "$out" "" "duplicate OpenClaw warning single root output"
|
||||
)
|
||||
|
||||
echo "==> case: ensure_pnpm (existing pnpm command)"
|
||||
(
|
||||
root="${TMP_DIR}/case-pnpm-existing"
|
||||
@ -480,8 +532,7 @@ echo "==> case: install_openclaw_from_git (deps step uses run_pnpm function)"
|
||||
# shellcheck disable=SC2034
|
||||
SHARP_IGNORE_GLOBAL_LIBVIPS=1
|
||||
|
||||
deps_called=0
|
||||
deps_cmd=""
|
||||
run_pnpm_steps=()
|
||||
|
||||
check_git() { return 0; }
|
||||
install_git() { fail "install_git should not be called"; }
|
||||
@ -498,16 +549,50 @@ echo "==> case: install_openclaw_from_git (deps step uses run_pnpm function)"
|
||||
run_quiet_step() {
|
||||
local _title="$1"
|
||||
shift
|
||||
if [[ "${_title}" == "Installing dependencies" ]]; then
|
||||
deps_called=1
|
||||
deps_cmd="${1:-}"
|
||||
if [[ "${1:-}" == "run_pnpm" ]]; then
|
||||
shift
|
||||
run_pnpm_steps+=("$*")
|
||||
return 0
|
||||
fi
|
||||
"$@" >/dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
install_openclaw_from_git "${repo}"
|
||||
assert_eq "$deps_called" "1" "install_openclaw_from_git dependencies step"
|
||||
assert_eq "$deps_cmd" "run_pnpm" "install_openclaw_from_git dependencies command"
|
||||
assert_eq "${run_pnpm_steps[0]}" "-C ${repo} install" "install_openclaw_from_git dependencies command"
|
||||
assert_eq "${run_pnpm_steps[1]}" "-C ${repo} ui:build" "install_openclaw_from_git ui build command"
|
||||
assert_eq "${run_pnpm_steps[2]}" "-C ${repo} build" "install_openclaw_from_git build command"
|
||||
)
|
||||
|
||||
echo "==> case: ensure_pnpm_git_prepare_allowlist (known dep added once)"
|
||||
(
|
||||
root="${TMP_DIR}/case-pnpm-git-prepare-allowlist"
|
||||
repo="${root}/repo"
|
||||
mkdir -p "${repo}"
|
||||
cat >"${repo}/pnpm-workspace.yaml" <<'EOF'
|
||||
packages:
|
||||
- .
|
||||
|
||||
onlyBuiltDependencies:
|
||||
- esbuild
|
||||
EOF
|
||||
cat >"${repo}/package.json" <<'EOF'
|
||||
{
|
||||
"name": "repo",
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
"esbuild"
|
||||
]
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
ensure_pnpm_git_prepare_allowlist "${repo}"
|
||||
ensure_pnpm_git_prepare_allowlist "${repo}"
|
||||
|
||||
workspace_count="$(grep -c '@tloncorp/api' "${repo}/pnpm-workspace.yaml" || true)"
|
||||
package_count="$(grep -c '@tloncorp/api' "${repo}/package.json" || true)"
|
||||
assert_eq "$workspace_count" "1" "ensure_pnpm_git_prepare_allowlist workspace count"
|
||||
assert_eq "$package_count" "1" "ensure_pnpm_git_prepare_allowlist package count"
|
||||
)
|
||||
|
||||
echo "==> case: install_openclaw_compat_shim (always uses user-local bin)"
|
||||
|
||||
338
src/components/TrustTopbar.astro
Normal file
338
src/components/TrustTopbar.astro
Normal file
@ -0,0 +1,338 @@
|
||||
---
|
||||
type PageKey = 'trust' | 'threatmodel';
|
||||
type Locale = 'en' | 'zh-cn' | 'ko' | 'ja';
|
||||
|
||||
interface LanguageLink {
|
||||
label: string;
|
||||
trust: string;
|
||||
threatmodel: string;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
activePage: PageKey;
|
||||
locale: Locale;
|
||||
wide?: boolean;
|
||||
labels: {
|
||||
trust: string;
|
||||
threatModel: string;
|
||||
};
|
||||
langLinks: Record<Locale, LanguageLink>;
|
||||
}
|
||||
|
||||
const { activePage, locale, wide = false, labels, langLinks } = Astro.props;
|
||||
|
||||
const languageOrder: Locale[] = ['en', 'zh-cn', 'ko', 'ja'];
|
||||
const currentLinks = langLinks[locale];
|
||||
const currentLanguageLabel = langLinks[locale].label;
|
||||
const languageEntries = languageOrder.map((language) => ({
|
||||
code: language,
|
||||
href: langLinks[language][activePage],
|
||||
label: langLinks[language].label,
|
||||
isActive: language === locale,
|
||||
}));
|
||||
---
|
||||
|
||||
<nav class="trust-topbar" aria-label="Trust site">
|
||||
<div class:list={['trust-topbar-inner', { wide }]}>
|
||||
<a href="https://openclaw.ai" class="trust-topbar-brand">
|
||||
<img src="/openclaw-logo-text-dark.png" alt="OpenClaw" />
|
||||
</a>
|
||||
|
||||
<div class="trust-topbar-nav">
|
||||
<div class="trust-topbar-tabs">
|
||||
<a
|
||||
href={currentLinks.trust}
|
||||
class:list={['trust-topbar-link', { active: activePage === 'trust' }]}
|
||||
aria-current={activePage === 'trust' ? 'page' : undefined}
|
||||
>
|
||||
{labels.trust}
|
||||
</a>
|
||||
<a
|
||||
href={currentLinks.threatmodel}
|
||||
class:list={['trust-topbar-link', { active: activePage === 'threatmodel' }]}
|
||||
aria-current={activePage === 'threatmodel' ? 'page' : undefined}
|
||||
>
|
||||
{labels.threatModel}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="trust-lang-switcher" aria-label="Languages">
|
||||
{languageEntries.map((language) => (
|
||||
<a
|
||||
href={language.href}
|
||||
class:list={['trust-lang-link', { active: language.isActive }]}
|
||||
aria-current={language.isActive ? 'page' : undefined}
|
||||
>
|
||||
{language.label}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<label class="trust-lang-select-wrap">
|
||||
<span class="sr-only">Language</span>
|
||||
<select
|
||||
class="trust-lang-select"
|
||||
aria-label="Language"
|
||||
onchange="if (this.value) window.location.href = this.value"
|
||||
>
|
||||
{languageEntries.map((language) => (
|
||||
<option
|
||||
value={language.href}
|
||||
selected={language.isActive}
|
||||
>
|
||||
{language.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<span class="trust-lang-select-label">{currentLanguageLabel}</span>
|
||||
<span class="trust-lang-select-icon" aria-hidden="true">▾</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<style>
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.trust-topbar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
background: var(--bg);
|
||||
border-bottom: 1px solid var(--gray-200);
|
||||
}
|
||||
|
||||
.trust-topbar-inner {
|
||||
max-width: 720px;
|
||||
margin: 0 auto;
|
||||
padding: 0 24px;
|
||||
min-height: 52px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.trust-topbar-inner.wide {
|
||||
max-width: 1800px;
|
||||
}
|
||||
|
||||
.trust-topbar-brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-decoration: none;
|
||||
color: var(--gray-900);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.trust-topbar-brand:hover {
|
||||
color: var(--gray-900);
|
||||
}
|
||||
|
||||
.trust-topbar-brand img {
|
||||
height: 28px;
|
||||
width: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.trust-topbar-nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: nowrap;
|
||||
gap: 12px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.trust-topbar-tabs {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: nowrap;
|
||||
gap: 4px;
|
||||
min-width: 0;
|
||||
flex: 0 1 auto;
|
||||
}
|
||||
|
||||
.trust-topbar-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 0;
|
||||
padding: 6px 14px;
|
||||
border-radius: 8px;
|
||||
font-family: var(--heading);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--gray-500);
|
||||
transition: color 0.15s, background 0.15s;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.trust-topbar-link:hover {
|
||||
color: var(--gray-900);
|
||||
background: var(--gray-50);
|
||||
}
|
||||
|
||||
.trust-topbar-link.active {
|
||||
color: var(--primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.trust-lang-switcher {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 2px;
|
||||
margin-left: 8px;
|
||||
padding-left: 10px;
|
||||
border-left: 1px solid var(--gray-200);
|
||||
}
|
||||
|
||||
.trust-lang-link {
|
||||
padding: 4px 8px;
|
||||
border-radius: 6px;
|
||||
font-family: var(--heading);
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: var(--gray-400);
|
||||
transition: color 0.15s, background 0.15s;
|
||||
}
|
||||
|
||||
.trust-lang-link:hover {
|
||||
color: var(--gray-900);
|
||||
background: var(--gray-50);
|
||||
}
|
||||
|
||||
.trust-lang-link.active {
|
||||
color: var(--primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.trust-lang-select-wrap {
|
||||
display: none;
|
||||
position: relative;
|
||||
flex: 0 0 86px;
|
||||
min-width: 86px;
|
||||
}
|
||||
|
||||
.trust-lang-select {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.trust-lang-select-label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
min-height: 34px;
|
||||
padding: 7px 28px 7px 10px;
|
||||
border: 1px solid var(--gray-200);
|
||||
border-radius: 8px;
|
||||
font-family: var(--heading);
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: var(--primary);
|
||||
background: var(--bg);
|
||||
}
|
||||
|
||||
.trust-lang-select-icon {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: var(--gray-400);
|
||||
pointer-events: none;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
@media (max-width: 820px) {
|
||||
.trust-topbar-inner {
|
||||
padding: 10px 16px;
|
||||
min-height: 52px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.trust-topbar-brand img {
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.trust-topbar-nav {
|
||||
flex: 1 1 auto;
|
||||
justify-content: flex-end;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.trust-topbar-tabs {
|
||||
flex: 0 1 auto;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.trust-topbar-link {
|
||||
flex: 0 1 auto;
|
||||
min-width: 0;
|
||||
padding: 8px 4px;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.trust-lang-switcher {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.trust-lang-select-wrap {
|
||||
display: block;
|
||||
flex: 0 0 80px;
|
||||
min-width: 80px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 380px) {
|
||||
.trust-topbar-inner {
|
||||
padding: 10px 12px;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.trust-topbar-brand img {
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.trust-topbar-nav {
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.trust-topbar-link {
|
||||
font-size: 11px;
|
||||
padding: 8px 2px;
|
||||
}
|
||||
|
||||
.trust-lang-select-wrap {
|
||||
flex-basis: 72px;
|
||||
min-width: 72px;
|
||||
}
|
||||
|
||||
.trust-lang-select-label {
|
||||
min-height: 32px;
|
||||
padding: 6px 24px 6px 8px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.trust-lang-select-icon {
|
||||
right: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1,4 +1,5 @@
|
||||
import { defineCollection, z } from 'astro:content';
|
||||
import { glob } from 'astro/loaders';
|
||||
|
||||
const authorSchema = z.object({
|
||||
name: z.string(),
|
||||
@ -6,7 +7,7 @@ const authorSchema = z.object({
|
||||
});
|
||||
|
||||
const blog = defineCollection({
|
||||
type: 'content',
|
||||
loader: glob({ pattern: '**/*.md', base: './src/content/blog' }),
|
||||
schema: z.object({
|
||||
title: z.string(),
|
||||
description: z.string(),
|
||||
33
src/content/blog/openclaw-rough-week.md
Normal file
33
src/content/blog/openclaw-rough-week.md
Normal file
@ -0,0 +1,33 @@
|
||||
---
|
||||
title: "OpenClaw Had a Rough Week"
|
||||
description: "What happened around the 2026.4.24 and 2026.4.29 releases, why the direction was right, and what we are changing now."
|
||||
date: 2026-05-05
|
||||
author: "Peter Steinberger"
|
||||
authorHandle: "steipete"
|
||||
draft: false
|
||||
tags: ["release", "stability", "security", "clawhub"]
|
||||
---
|
||||
|
||||
**TL;DR:** OpenClaw had a rough week. 2026.4.29 made it obvious. Sorry. We are making core smaller, moving optional stuff to [ClawHub](https://clawhub.ai/), and announcing LTS separately later in May.
|
||||
|
||||
The trouble started around 2026.4.24. By 2026.4.29 it was obvious enough that nobody could pretend this was just a few weird installs. Gateways got slower. Some installs got stuck in plugin dependency repair loops. Discord, Telegram, WhatsApp and other channels behaved worse than they should. People downgraded. People lost time.
|
||||
|
||||
This was not one bug. Plugin dependency repair ran in startup and update paths, bundled and external plugins were half-split, ClawHub artifact metadata was still settling, and gateway cold paths did too much work.
|
||||
|
||||
That sucks. I’m sorry.
|
||||
|
||||
We’ve been pushing OpenClaw to become smaller, safer and more infrastructure-grade. That means less magic in core, fewer bundled dependencies, clearer plugin boundaries, better scanning, better release hygiene, better security posture. All the boring stuff that matters once people run this as actual infrastructure and not just as my weird lobster playground.
|
||||
|
||||
Recent npm ecosystem supply-chain incidents made this feel a lot less theoretical. OpenClaw did not directly depend on [Axios](https://www.microsoft.com/en-us/security/blog/2026/04/01/mitigating-the-axios-npm-supply-chain-compromise/); the relevant risk was the shape of the dependency graph: transitive packages, install-time behavior, postinstall scripts, packages pulling packages pulling packages.
|
||||
|
||||
So we started moving things out of core: channels, providers, heavy tools, parsers, optional integrations. The [plugin inventory](https://docs.openclaw.ai/plugins/plugin-inventory) shows what still ships in core, what installs separately, and what is source-checkout only.
|
||||
|
||||
The problem: I underestimated how difficult it would be to get this right. For a few releases we ended up in the worst middle state: too much moved toward plugins, while too many plugins were still bundled, repaired, staged, checked, or dependency-loaded in places users feel immediately.
|
||||
|
||||
This also exposed an operating problem: OpenClaw was still too founder-driven. Too much release, review, packaging and support work sat with me. Through the OpenClaw Foundation, and with help from OpenAI, we are building a real team around the project.
|
||||
|
||||
Going forward, we'll be changing how releases are done, and will soon announce an LTS release next to our faster update cycles.
|
||||
|
||||
Thank you to everyone who reported issues, pasted logs, tested betas, downgraded, upgraded again, or just waited while we dug through this.
|
||||
|
||||
OpenClaw will keep getting more secure. It will also get smaller. But it has to stay boringly reliable while we do that.
|
||||
83
src/content/blog/openclaw-security-in-public.md
Normal file
83
src/content/blog/openclaw-security-in-public.md
Normal file
@ -0,0 +1,83 @@
|
||||
---
|
||||
title: "How OpenClaw Got Safer in Public"
|
||||
description: "The work you do not see behind the world's most-watched open source personal AI agent."
|
||||
date: 2026-04-30
|
||||
author: "Peter Steinberger"
|
||||
authorHandle: "steipete"
|
||||
draft: false
|
||||
tags: ["security", "open-source", "nvidia", "clawhub"]
|
||||
---
|
||||
|
||||
OpenClaw started on my Mac in Vienna as an experiment. A lot of people screamed it was so insecure.
|
||||
|
||||
Open source is supposed to be the unsafe option because everyone can see the code. Sure.
|
||||
|
||||
People used it anyway, loved it, and now companies run it in production. Those same companies are the ones now helping us secure it. Nothing that can run tools, hold credentials and install plugins is safe by default. But being open is why we got safer quickly, in public.
|
||||
|
||||
## Why So Many Reports?
|
||||
|
||||
OpenClaw launched into a weird moment for open source security. In January, curl killed its bug bounty program after drowning in reports that sounded technical, referenced real functions and contained nothing exploitable. Daniel Stenberg called it "death by a thousand slops."
|
||||
|
||||
Plus, we are the most-watched AI agent project in the world. Every CVE against OpenClaw is a career trophy, so of course people look.
|
||||
|
||||
As of April 30, GitHub shows [1,309 security advisories](https://github.com/openclaw/openclaw/security/advisories) since January 10. 535 were published. 746 were closed as invalid. The number coming in has dropped significantly over the last few months as we hardened the whole system.
|
||||
|
||||
The closer a report sits to "critical", the more likely it is to be nonsense. GitHub currently shows 109 critical reports: 14 published, 95 closed as invalid. That is 87%.
|
||||
|
||||
The false positives are often wonderfully dumb: "the agent runs commands, therefore RCE", "plugins execute code", "this dangerous opt-in mode is dangerous", "if I already have the token I can do bad things."
|
||||
|
||||
## What Actually Changed
|
||||
|
||||
At first I was just annoyed at how the game worked. A security advisory used to be an event: stop everything, reproduce, inspect, patch, disclose, ship. Five times a year was annoying; fifteen times a day breaks the process.
|
||||
|
||||
What we needed was a triage tool, not a magical sandbox: a way to decide whether a report describes a real boundary violation or OpenClaw doing expected OpenClaw things. [SECURITY.md](https://github.com/openclaw/openclaw/blob/main/SECURITY.md) defines the trust model, documents expected behavior, and gives maintainers something concrete to point at when closing bad reports.
|
||||
|
||||
Real bugs remain. OpenClaw moves fast and does weird stuff. We fixed authentication bugs, privilege confusion, reconnect scope widening, sandbox bypasses, unsafe env handling and approval path mistakes.
|
||||
|
||||
Some of this cost regular users features. We tightened allowlists, accepted regressions where the single-machine setup (the Mac Mini on your desk, your laptop) was fine, and shipped fast even when fast hurt. Most of the hardening targets multi-user threats most users never hit. We did it anyway, because the people who do hit them are now running this in production.
|
||||
|
||||
## Built for Production
|
||||
|
||||
We shrank the core. Over the last few months we pushed more functionality out to [plugins](https://docs.openclaw.ai/plugins/sdk-overview), which means a smaller attack surface, a shorter dependency tree and a clearer trust boundary. A poisoned upstream package has fewer paths to actually reach a user.
|
||||
|
||||
Releases used to be just me. Now it's me plus another [OpenClaw Foundation](https://www.openclaw.org/) employee, with each one scripted, gated and signed off. End-to-end testing in CI got leveled up so agent flows run on every PR instead of waiting for someone's laptop.
|
||||
|
||||
We added [observability](https://docs.openclaw.ai/gateway/opentelemetry): OpenTelemetry, Prometheus metrics, higher-throughput logging and better signals. Secrets moved away from "please be careful" toward references, so credentials do not end up sitting in prompts, logs, transcripts or agent state.
|
||||
|
||||
Plugins can act as harnesses now. Wire OpenAI Codex in as [the harness](https://docs.openclaw.ai/plugins/codex-harness) for GPT models and you inherit its controls, including Guardian for per-action gating, instead of running the agent in accept-each-request or YOLO mode.
|
||||
|
||||
## The Team Behind It
|
||||
|
||||
OpenClaw is not just me anymore. It's me plus an army of maintainers who triage reports, review patches, ship releases and take calls at stupid hours when something real lands. Most have day jobs. They still show up.
|
||||
|
||||
They have help. CodeQL, Semgrep, Codex Security and maintainer-owned checks catch weak commits before they merge. [ClawSweeper](https://github.com/openclaw/clawsweeper) handles issue and PR triage so the team can keep up with the firehose.
|
||||
|
||||
[NVIDIA](https://blogs.nvidia.com/blog/what-openclaw-agents-mean-for-every-organization/) showed up early with engineering time, security thinking and work on [NemoClaw](https://docs.nvidia.com/nemoclaw/latest/) and OpenShell.
|
||||
|
||||
[Microsoft](https://www.microsoft.com/) and [GitHub](https://github.com/) helped at the platform level through the [GitHub Secure Open Source Fund](https://github.blog/open-source/maintainers/securing-the-ai-software-supply-chain-security-results-across-67-open-source-projects/). [Atlassian](https://www.atlassian.com/) and other enterprise partners pushed on deployment, auditability, identity boundaries and secret handling. [Blacksmith](https://www.blacksmith.sh/) gives us the runner capacity to test agent paths at the rate we ship.
|
||||
|
||||
[Tencent](https://www.tencent.com/) added full-time maintainers on security, stability and ClawHub, plus a direct vulnerability-sync line with their internal security team.
|
||||
|
||||
[OpenAI](https://openai.com/) continues to support the project with inference, gave us [Codex Security](https://openai.com/index/codex-security-now-in-research-preview/) to proactively find and fix security issues, and has made commitments that help keep OpenClaw open and independent as the Foundation comes together. Inside OpenAI, I run a team called Claw Labs that works on shared product improvements.
|
||||
|
||||
## ClawHub
|
||||
|
||||
[Convex](https://www.convex.dev/) helped maintain ClawHub while we rebuilt the security posture around it. You do not secure marketplaces once. You keep watching, pruning and making the weird stuff easier to spot.
|
||||
|
||||
In the last month alone the team closed more than 700 ClawHub moderation issues, around 460 of them rescan appeals from skill authors whose work the automated suspicious flag had misfired on. We will publish more of the ClawHub security findings soon.
|
||||
|
||||
## Agents of Chaos
|
||||
|
||||
The [Agents of Chaos](https://arxiv.org/abs/2602.20021) paper that made the rounds in February is the loudest example of the incentive problem. Twenty researchers attacked six OpenClaw agents for two weeks and found ugly failures.
|
||||
|
||||
The annoying part is the framing. They ran OpenClaw in sudo mode with disabled guardrails, broad shell access and no sandboxing, then wrote up the results as if this is what users get out of the box. The paper has since added a short acknowledgment that guardrails were disabled; the headlines did not.
|
||||
|
||||
The lesson is simpler. OpenClaw is built for one trusted person per agent. Share that agent with people you don't trust, and they share its tool access. That is the design, not a hidden auth bug. For groups or companies, split agents and credentials per trust boundary, and turn on sandboxing.
|
||||
|
||||
## Fixes Count
|
||||
|
||||
The security industry rewards disclosure, not repair. To researchers: I would much rather read your slightly broken report with a real reproduction than your perfectly formatted slop. "I found and fixed a vulnerability in OpenClaw" should carry more credit than "I filed the scariest GHSA title."
|
||||
|
||||
Open and safe are not opposites. Open is how we get to safe at all.
|
||||
|
||||
The claw is the law. 🦞
|
||||
62
src/data/community-builds.json
Normal file
62
src/data/community-builds.json
Normal file
@ -0,0 +1,62 @@
|
||||
[
|
||||
{
|
||||
"id": "2012778049406742632",
|
||||
"author": "jdrhyne",
|
||||
"title": "15+ Agent Army Running Across 3 Machines",
|
||||
"href": "https://x.com/jdrhyne/status/2012778049406742632",
|
||||
"source": "X thread",
|
||||
"summary": "A full OpenClaw setup clearing 10,000 emails, reviewing decks, building CLI tools, optimizing Google Ads, drafting posts, and orchestrating Codex workers across a Discord-driven agent fleet.",
|
||||
"tags": ["multi-agent", "discord", "automation"],
|
||||
"likes": 119
|
||||
},
|
||||
{
|
||||
"id": "2012565160586625345",
|
||||
"author": "danpeguine",
|
||||
"title": "Personal Operating System With Daily Briefings",
|
||||
"href": "https://x.com/danpeguine/status/2012565160586625345",
|
||||
"source": "X thread",
|
||||
"summary": "OpenClaw timeblocks tasks, runs weekly reviews from meeting notes, briefs before meetings, watches family school deadlines, researches projects, resolves calendar conflicts, and creates invoices.",
|
||||
"tags": ["calendar", "briefings", "productivity"],
|
||||
"likes": 257
|
||||
},
|
||||
{
|
||||
"id": "2012119327147798753",
|
||||
"author": "georgedagg_",
|
||||
"title": "Voice-Guided Production Fix While Walking the Dog",
|
||||
"href": "https://x.com/georgedagg_/status/2012119327147798753",
|
||||
"source": "X thread",
|
||||
"summary": "A live OpenClaw workflow that inspected failed Railway builds, diagnosed the root cause, changed deployment configs, redeployed, fixed a design issue, and submitted a PR over voice.",
|
||||
"tags": ["voice", "deploy", "coding"],
|
||||
"likes": 143
|
||||
},
|
||||
{
|
||||
"id": "2012535486401671588",
|
||||
"author": "dreetje",
|
||||
"title": "One Chat Controlling Mail, Messages, Orders, Calls, and a Vault",
|
||||
"href": "https://x.com/dreetje/status/2012535486401671588",
|
||||
"source": "X thread",
|
||||
"summary": "A single OpenClaw assistant checking mail, reading Beeper messages, ordering things, sending reminders, creating GitHub issues, discussing bookmarks, handling voice calls, and reading and writing a dedicated 1Password vault.",
|
||||
"tags": ["mail", "beeper", "personal-ai"],
|
||||
"likes": 271
|
||||
},
|
||||
{
|
||||
"id": "2008994096736817624",
|
||||
"author": "davekiss",
|
||||
"title": "Entire Website Rebuilt From Telegram",
|
||||
"href": "https://x.com/davekiss/status/2008994096736817624",
|
||||
"source": "X thread",
|
||||
"summary": "A full personal-site rebuild from bed: Notion to Astro, 18 posts migrated, DNS moved to Cloudflare, and no laptop involved.",
|
||||
"tags": ["telegram", "website", "astro"],
|
||||
"likes": 142
|
||||
},
|
||||
{
|
||||
"id": "2007616854689280196",
|
||||
"author": "stevecaldwell",
|
||||
"title": "Family Meal Planning System Built Overnight",
|
||||
"href": "https://x.com/stevecaldwell/status/2007616854689280196",
|
||||
"source": "X thread",
|
||||
"summary": "A weekly family planning system with a year-long meal template, aisle-sorted shopping lists, weather-aware dinner planning, and automated reminders.",
|
||||
"tags": ["family", "notion", "planning"],
|
||||
"likes": 133
|
||||
}
|
||||
]
|
||||
65
src/data/press.json
Normal file
65
src/data/press.json
Normal file
@ -0,0 +1,65 @@
|
||||
[
|
||||
{
|
||||
"outlet": "TechCrunch",
|
||||
"title": "OpenClaw’s AI assistants are now building their own social network",
|
||||
"url": "https://techcrunch.com/2026/01/30/openclaws-ai-assistants-are-now-building-their-own-social-network/",
|
||||
"displayDate": "Jan 30, 2026",
|
||||
"author": "Anna Heim",
|
||||
"summary": "A strong first-read on the OpenClaw takeoff: rebrand, viral growth, Moltbook, and why the project captured the open-source AI crowd.",
|
||||
"featured": true
|
||||
},
|
||||
{
|
||||
"outlet": "TechCrunch",
|
||||
"title": "OpenClaw creator Peter Steinberger joins OpenAI",
|
||||
"url": "https://techcrunch.com/2026/02/15/openclaw-creator-peter-steinberger-joins-openai/",
|
||||
"displayDate": "Feb 15, 2026",
|
||||
"author": "Anthony Ha",
|
||||
"summary": "A clean, positive headline around the next chapter for OpenClaw: founder momentum, OpenAI backing, and the long-term foundation path.",
|
||||
"featured": true
|
||||
},
|
||||
{
|
||||
"outlet": "The Verge",
|
||||
"title": "The OpenClaw superfan meetup serves optimism and lobster",
|
||||
"url": "https://www.theverge.com/ai-artificial-intelligence/890517/openclaw-clawcon-meetup-nyc-open-source-ai",
|
||||
"displayDate": "Mar 7, 2026",
|
||||
"author": "The Verge",
|
||||
"summary": "A strong community story about ClawCon, the people building around OpenClaw, and the feeling that personal agents have escaped the lab.",
|
||||
"featured": false
|
||||
},
|
||||
{
|
||||
"outlet": "The Verge",
|
||||
"title": "OpenClaw creator Peter Steinberger joins OpenAI",
|
||||
"url": "https://www.theverge.com/openai/630450/peter-steinberger-openclaw-openai",
|
||||
"displayDate": "Feb 15, 2026",
|
||||
"author": "The Verge",
|
||||
"summary": "Another positive institutional signal: OpenClaw’s creator moving into a major platform role while the project continues as open source.",
|
||||
"featured": false
|
||||
},
|
||||
{
|
||||
"outlet": "Business Insider",
|
||||
"title": "OpenClaw's creator is heading to OpenAI. He says it could've been a 'huge company,' but building one didn't excite him.",
|
||||
"url": "https://www.businessinsider.com/sam-altman-hires-openclaw-creator-peter-steinberger-personal-ai-agents-2026-2",
|
||||
"displayDate": "Feb 15, 2026",
|
||||
"author": "Business Insider",
|
||||
"summary": "A positive business-profile angle on OpenClaw’s breakout moment and why Steinberger chose leverage and reach over starting another company.",
|
||||
"featured": false
|
||||
},
|
||||
{
|
||||
"outlet": "Business Insider",
|
||||
"title": "'Eccentric but brilliant': OpenClaw's creator got feedback from Mark Zuckerberg",
|
||||
"url": "https://www.businessinsider.com/openclaw-creator-peter-steinberger-gets-feedback-from-mark-zuckerberg",
|
||||
"displayDate": "Feb 19, 2026",
|
||||
"author": "Business Insider",
|
||||
"summary": "Good social proof for the site: OpenClaw getting attention and detailed product feedback from the very top of the industry.",
|
||||
"featured": false
|
||||
},
|
||||
{
|
||||
"outlet": "TechCrunch",
|
||||
"title": "OpenClaw creator’s advice to AI builders is to be more playful and allow yourself time to improve",
|
||||
"url": "https://techcrunch.com/2026/02/25/openclaw-creators-advice-to-ai-builders-is-to-be-more-playful-and-allow-yourself-time-to-improve/",
|
||||
"displayDate": "Feb 25, 2026",
|
||||
"author": "Sarah Perez",
|
||||
"summary": "A builder-focused piece with the right tone for the homepage: experimentation, agency, and why OpenClaw resonated with makers so quickly.",
|
||||
"featured": false
|
||||
}
|
||||
]
|
||||
@ -6,7 +6,7 @@ import { getPublishedBlogPosts } from '../../lib/blog';
|
||||
export async function getStaticPaths() {
|
||||
const posts = await getPublishedBlogPosts();
|
||||
return posts.map((post) => ({
|
||||
params: { slug: post.slug },
|
||||
params: { slug: post.id },
|
||||
props: { post },
|
||||
}));
|
||||
}
|
||||
@ -38,7 +38,7 @@ function estimateReadTime(content: string): string {
|
||||
}
|
||||
|
||||
const readTime = estimateReadTime(post.body || '');
|
||||
const postUrl = `https://openclaw.ai/blog/${post.slug}`;
|
||||
const postUrl = `https://openclaw.ai/blog/${post.id}`;
|
||||
---
|
||||
|
||||
<Layout title={`${post.data.title} — OpenClaw Blog`} description={post.data.description}>
|
||||
@ -117,6 +117,8 @@ const postUrl = `https://openclaw.ai/blog/${post.slug}`;
|
||||
<span class="footer-separator">·</span>
|
||||
<a href="/showcase">Showcase</a>
|
||||
<span class="footer-separator">·</span>
|
||||
<a href="/press">Press</a>
|
||||
<span class="footer-separator">·</span>
|
||||
<a href="/shoutouts">Shoutouts</a>
|
||||
<span class="footer-separator">·</span>
|
||||
<a href="https://trust.openclaw.ai/">Trust</a>
|
||||
|
||||
@ -36,7 +36,7 @@ function estimateReadTime(content: string): string {
|
||||
|
||||
<div class="posts-grid">
|
||||
{posts.map((post) => (
|
||||
<a href={`/blog/${post.slug}`} class="post-card">
|
||||
<a href={`/blog/${post.id}`} class="post-card">
|
||||
<div class="post-meta">
|
||||
<span class="post-date">{formatDate(post.data.date)}</span>
|
||||
<span class="post-separator">·</span>
|
||||
@ -70,6 +70,8 @@ function estimateReadTime(content: string): string {
|
||||
<span class="footer-separator">·</span>
|
||||
<a href="/showcase">Showcase</a>
|
||||
<span class="footer-separator">·</span>
|
||||
<a href="/press">Press</a>
|
||||
<span class="footer-separator">·</span>
|
||||
<a href="/shoutouts">Shoutouts</a>
|
||||
<span class="footer-separator">·</span>
|
||||
<a href="/integrations">Integrations</a>
|
||||
|
||||
26
src/pages/cat.astro
Normal file
26
src/pages/cat.astro
Normal file
@ -0,0 +1,26 @@
|
||||
---
|
||||
// meow
|
||||
---
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>🐱</title>
|
||||
<link rel="icon" href="/cat.ico" />
|
||||
<meta property="og:title" content="OpenClaw" />
|
||||
<meta property="og:description" content="Because Siri wasn't answering at 3AM." />
|
||||
<meta property="og:image" content="https://openclaw.ai/og-image.png" />
|
||||
<meta property="og:image:width" content="1200" />
|
||||
<meta property="og:image:height" content="630" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content="https://openclaw.ai/cat" />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:title" content="OpenClaw" />
|
||||
<meta name="twitter:description" content="Because Siri wasn't answering at 3AM." />
|
||||
<meta name="twitter:image" content="https://openclaw.ai/og-image.png" />
|
||||
<meta http-equiv="refresh" content="0; url=https://www.youtube.com/watch?v=hvL1339luv0" />
|
||||
</head>
|
||||
<body>
|
||||
<script>window.location.href="https://www.youtube.com/watch?v=hvL1339luv0";</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -3,6 +3,8 @@ import Layout from '../layouts/Layout.astro';
|
||||
import Icon from '../components/Icon.astro';
|
||||
import SectionHeader from '../components/SectionHeader.astro';
|
||||
import testimonials from '../data/testimonials.json';
|
||||
import communityBuilds from '../data/community-builds.json';
|
||||
import pressArticles from '../data/press.json';
|
||||
import { getPublishedBlogPosts } from '../lib/blog';
|
||||
|
||||
// Get latest blog post
|
||||
@ -28,10 +30,10 @@ const integrationPills = [
|
||||
{ name: 'Spotify', icon: siIcon(siSpotify), color: '#1DB954' },
|
||||
{ name: 'Hue', icon: siIcon(siPhilipshue), color: '#0065D3' },
|
||||
{ name: 'Obsidian', icon: siIcon(siObsidian), color: '#7C3AED' },
|
||||
{ name: 'Twitter', icon: siIcon(siX), color: '#FFFFFF' },
|
||||
{ name: 'Twitter', icon: siIcon(siX), color: 'var(--text-primary)' },
|
||||
{ name: 'Browser', icon: siIcon(siGooglechrome), color: '#4285F4' },
|
||||
{ name: 'Gmail', icon: siIcon(siGmail), color: '#EA4335' },
|
||||
{ name: 'GitHub', icon: siIcon(siGithub), color: '#FFFFFF' },
|
||||
{ name: 'GitHub', icon: siIcon(siGithub), color: 'var(--text-primary)' },
|
||||
];
|
||||
|
||||
// Split top 30 into two rows for carousel
|
||||
@ -49,6 +51,8 @@ const pixelsPerItem = 336;
|
||||
const pixelsPerSecond = 50;
|
||||
const duration1 = (row1.length / 2 * pixelsPerItem) / pixelsPerSecond;
|
||||
const duration2 = (row2.length / 2 * pixelsPerItem) / pixelsPerSecond;
|
||||
const featuredBuilds = communityBuilds.slice(0, 4);
|
||||
const featuredPress = pressArticles.slice(0, 4);
|
||||
---
|
||||
|
||||
<Layout title="OpenClaw — Personal AI Assistant">
|
||||
@ -98,7 +102,7 @@ const duration2 = (row2.length / 2 * pixelsPerItem) / pixelsPerSecond;
|
||||
<!-- Latest Blog Post -->
|
||||
{latestPost && (
|
||||
<section class="latest-post">
|
||||
<a href={`/blog/${latestPost.slug}`} class="latest-post-card">
|
||||
<a href={`/blog/${latestPost.id}`} class="latest-post-card">
|
||||
<span class="latest-post-badge">New</span>
|
||||
<span class="latest-post-title">{latestPost.data.title}</span>
|
||||
<span class="latest-post-link">→</span>
|
||||
@ -172,18 +176,10 @@ const duration2 = (row2.length / 2 * pixelsPerItem) / pixelsPerSecond;
|
||||
<button class="hackable-btn active" data-hackable="installer">installer</button>
|
||||
<button class="hackable-btn" data-hackable="pnpm">pnpm</button>
|
||||
</div>
|
||||
<div class="os-indicator" id="os-indicator">
|
||||
<span class="os-detected" id="os-detected">macOS/Linux</span>
|
||||
<button class="os-change-btn" id="os-change-btn">change</button>
|
||||
</div>
|
||||
<div class="os-switch" id="os-switch" style="display: none;">
|
||||
<button class="os-btn active" data-os="unix">macOS/Linux</button>
|
||||
<button class="os-btn active" data-os="unix">macOS & Linux</button>
|
||||
<button class="os-btn" data-os="windows">Windows</button>
|
||||
</div>
|
||||
<div class="win-shell-switch" id="win-shell-switch" style="display: none;">
|
||||
<button class="win-shell-btn active" data-shell="powershell">PowerShell</button>
|
||||
<button class="win-shell-btn" data-shell="cmd">CMD</button>
|
||||
</div>
|
||||
<div class="beta-switch" id="beta-switch">
|
||||
<button class="beta-btn" id="beta-btn" data-beta="false">
|
||||
<span class="beta-label">β</span>
|
||||
@ -236,7 +232,7 @@ const duration2 = (row2.length / 2 * pixelsPerItem) / pixelsPerSecond;
|
||||
</div>
|
||||
</div>
|
||||
<div id="hackable-pnpm-content" style="display: none;">
|
||||
<div class="code-line comment"># You clearly know what you're doing</div>
|
||||
<div class="code-line comment"># Source checkouts are pnpm workspaces</div>
|
||||
<div class="code-line cmd">
|
||||
<span class="code-prompt">$</span>
|
||||
<span>git clone https://github.com/openclaw/openclaw.git</span>
|
||||
@ -247,16 +243,16 @@ const duration2 = (row2.length / 2 * pixelsPerItem) / pixelsPerSecond;
|
||||
</div>
|
||||
<div class="code-line cmd">
|
||||
<span class="code-prompt">$</span>
|
||||
<span>cd openclaw && pnpm install && pnpm run build</span>
|
||||
<span>cd openclaw && corepack enable && pnpm install</span>
|
||||
<button class="copy-line-btn" data-cmd="build" title="Copy">
|
||||
<svg class="copy-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
|
||||
<svg class="check-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="display:none"><polyline points="20 6 9 17 4 12"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="code-line comment"># You built it, now meet it</div>
|
||||
<div class="code-line comment"># Run from source; bundled plugins use extensions/* deps</div>
|
||||
<div class="code-line cmd">
|
||||
<span class="code-prompt">$</span>
|
||||
<span>pnpm run openclaw onboard</span>
|
||||
<span>pnpm openclaw onboard</span>
|
||||
<button class="copy-line-btn" data-cmd="hackable-onboard" title="Copy">
|
||||
<svg class="copy-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
|
||||
<svg class="check-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="display:none"><polyline points="20 6 9 17 4 12"/></svg>
|
||||
@ -283,30 +279,21 @@ const duration2 = (row2.length / 2 * pixelsPerItem) / pixelsPerSecond;
|
||||
</div>
|
||||
</div>
|
||||
<p class="quickstart-note">
|
||||
Works on macOS, Windows & Linux. The one-liner installs Node.js and everything else for you.
|
||||
Works on macOS, Linux, and Windows. The one-liner installs Node.js and everything else for you. Switch later with <code>openclaw update --channel dev</code> or <code>openclaw update --channel stable</code>.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
// Windows install commands
|
||||
const windowsPsCmd = 'iwr -useb https://openclaw.ai/install.ps1 | iex';
|
||||
const windowsPsBetaCmd = '& ([scriptblock]::Create((iwr -useb https://openclaw.ai/install.ps1))) -Tag beta';
|
||||
const windowsCmdCmd = 'curl -fsSL https://openclaw.ai/install.cmd -o install.cmd && install.cmd && del install.cmd';
|
||||
const windowsCmdBetaCmd = 'curl -fsSL https://openclaw.ai/install.cmd -o install.cmd && install.cmd --tag beta && del install.cmd';
|
||||
|
||||
const osLabels = {
|
||||
unix: 'macOS/Linux',
|
||||
windows: 'Windows'
|
||||
};
|
||||
const windowsCmd = 'powershell -c "irm https://openclaw.ai/install.ps1 | iex"';
|
||||
const windowsBetaCmd = 'powershell -c "& ([scriptblock]::Create((irm https://openclaw.ai/install.ps1))) -Tag beta"';
|
||||
const windowsGitCmd = 'powershell -c "& ([scriptblock]::Create((irm https://openclaw.ai/install.ps1))) -InstallMethod git"';
|
||||
|
||||
// State
|
||||
let currentPm = 'npm';
|
||||
let currentMode = 'oneliner';
|
||||
let currentHackable = 'installer';
|
||||
let currentBeta = false;
|
||||
let osPickerExpanded = false;
|
||||
let currentWinShell = 'powershell';
|
||||
|
||||
// Auto-detect OS using modern API with fallback
|
||||
const isWindows = navigator.userAgentData?.platform === 'Windows' ||
|
||||
navigator.userAgent.toLowerCase().includes('windows');
|
||||
@ -316,15 +303,10 @@ const duration2 = (row2.length / 2 * pixelsPerItem) / pixelsPerSecond;
|
||||
const pmBtns = document.querySelectorAll('.pm-btn');
|
||||
const hackableBtns = document.querySelectorAll('.hackable-btn');
|
||||
const osBtns = document.querySelectorAll('.os-btn');
|
||||
const winShellBtns = document.querySelectorAll('.win-shell-btn');
|
||||
const modeBtns = document.querySelectorAll('.mode-btn');
|
||||
const pmSwitch = document.getElementById('pm-switch');
|
||||
const hackableSwitch = document.getElementById('hackable-switch');
|
||||
const osSwitch = document.getElementById('os-switch');
|
||||
const winShellSwitch = document.getElementById('win-shell-switch');
|
||||
const osIndicator = document.getElementById('os-indicator');
|
||||
const osDetected = document.getElementById('os-detected');
|
||||
const osChangeBtn = document.getElementById('os-change-btn');
|
||||
const betaSwitch = document.getElementById('beta-switch');
|
||||
const betaBtn = document.getElementById('beta-btn');
|
||||
const switchPlaceholder = document.getElementById('switch-placeholder');
|
||||
@ -368,23 +350,21 @@ const duration2 = (row2.length / 2 * pixelsPerItem) / pixelsPerSecond;
|
||||
? `npm i -g openclaw${betaSuffix}`
|
||||
: `pnpm add -g openclaw${betaSuffix}`;
|
||||
pmInstallElements.forEach(cmd => cmd.textContent = installCmd);
|
||||
// Update one-liner OS command (with beta and shell support)
|
||||
// Update one-liner OS command (with beta support)
|
||||
let onelinerCmd;
|
||||
if (currentOs === 'unix') {
|
||||
onelinerCmd = currentBeta
|
||||
? "curl -fsSL https://openclaw.ai/install.sh | bash -s -- --beta"
|
||||
: "curl -fsSL https://openclaw.ai/install.sh | bash";
|
||||
} else if (currentWinShell === 'cmd') {
|
||||
onelinerCmd = currentBeta ? windowsCmdBetaCmd : windowsCmdCmd;
|
||||
} else {
|
||||
onelinerCmd = currentBeta ? windowsPsBetaCmd : windowsPsCmd;
|
||||
onelinerCmd = currentBeta ? windowsBetaCmd : windowsCmd;
|
||||
}
|
||||
osCmdElements.forEach(cmd => cmd.textContent = onelinerCmd);
|
||||
// Update hackable OS command for installer mode
|
||||
const hackableOsCmd = "curl -fsSL https://openclaw.ai/install.sh | bash -s -- --install-method git";
|
||||
const hackableOsCmd = currentOs === 'windows'
|
||||
? windowsGitCmd
|
||||
: "curl -fsSL https://openclaw.ai/install.sh | bash -s -- --install-method git";
|
||||
osCmdHackableElements.forEach(cmd => cmd.textContent = hackableOsCmd);
|
||||
// Update OS indicator text (null-safe)
|
||||
if (osDetected) osDetected.textContent = osLabels[currentOs];
|
||||
// Update hackable content visibility (null-safe)
|
||||
if (currentMode === 'hackable') {
|
||||
if (hackableInstallerContent) hackableInstallerContent.style.display = currentHackable === 'installer' ? 'block' : 'none';
|
||||
@ -409,18 +389,13 @@ const duration2 = (row2.length / 2 * pixelsPerItem) / pixelsPerSecond;
|
||||
if (codeHackable) codeHackable.style.display = currentMode === 'hackable' ? 'block' : 'none';
|
||||
if (codeMacos) codeMacos.style.display = currentMode === 'macos' ? 'block' : 'none';
|
||||
|
||||
// Show OS indicator for one-liner, PM switch for quick, hackable switch for hackable, nothing for macos
|
||||
const showOsControls = currentMode === 'oneliner';
|
||||
// Show OS switch for one-liner and installer-backed hackable mode.
|
||||
const showOsControls = currentMode === 'oneliner' || (currentMode === 'hackable' && currentHackable === 'installer');
|
||||
const showPmControls = currentMode === 'quick';
|
||||
const showHackableControls = currentMode === 'hackable';
|
||||
// Beta only applies to oneliner and npm modes (git always gets main branch)
|
||||
const showBetaControls = currentMode === 'oneliner' || currentMode === 'quick';
|
||||
// Show Windows shell toggle when Windows is selected in one-liner mode
|
||||
const showWinShellControls = showOsControls && currentOs === 'windows';
|
||||
|
||||
if (osIndicator) osIndicator.style.display = showOsControls && !osPickerExpanded ? 'flex' : 'none';
|
||||
if (osSwitch) osSwitch.style.display = showOsControls && osPickerExpanded ? 'flex' : 'none';
|
||||
if (winShellSwitch) winShellSwitch.style.display = showWinShellControls ? 'flex' : 'none';
|
||||
if (osSwitch) osSwitch.style.display = showOsControls ? 'flex' : 'none';
|
||||
if (pmSwitch) pmSwitch.style.display = showPmControls ? 'flex' : 'none';
|
||||
if (hackableSwitch) hackableSwitch.style.display = showHackableControls ? 'flex' : 'none';
|
||||
if (betaSwitch) betaSwitch.style.display = showBetaControls ? 'flex' : 'none';
|
||||
@ -429,14 +404,6 @@ const duration2 = (row2.length / 2 * pixelsPerItem) / pixelsPerSecond;
|
||||
if (switchPlaceholder) switchPlaceholder.style.display = noSwitchesVisible ? 'block' : 'none';
|
||||
}
|
||||
|
||||
// OS change toggle (null-safe)
|
||||
if (osChangeBtn) {
|
||||
osChangeBtn.addEventListener('click', () => {
|
||||
osPickerExpanded = true;
|
||||
updateVisibility();
|
||||
});
|
||||
}
|
||||
|
||||
// Beta toggle (null-safe)
|
||||
if (betaBtn) {
|
||||
betaBtn.addEventListener('click', () => {
|
||||
@ -460,6 +427,7 @@ const duration2 = (row2.length / 2 * pixelsPerItem) / pixelsPerSecond;
|
||||
hackableBtns.forEach(b => b.classList.remove('active'));
|
||||
btn.classList.add('active');
|
||||
updateCommands();
|
||||
updateVisibility();
|
||||
});
|
||||
});
|
||||
|
||||
@ -468,17 +436,6 @@ const duration2 = (row2.length / 2 * pixelsPerItem) / pixelsPerSecond;
|
||||
currentOs = btn.dataset.os;
|
||||
osBtns.forEach(b => b.classList.remove('active'));
|
||||
btn.classList.add('active');
|
||||
osPickerExpanded = false; // Collapse after selection
|
||||
updateCommands();
|
||||
updateVisibility();
|
||||
});
|
||||
});
|
||||
|
||||
winShellBtns.forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
currentWinShell = btn.dataset.shell;
|
||||
winShellBtns.forEach(b => b.classList.remove('active'));
|
||||
btn.classList.add('active');
|
||||
updateCommands();
|
||||
});
|
||||
});
|
||||
@ -488,7 +445,6 @@ const duration2 = (row2.length / 2 * pixelsPerItem) / pixelsPerSecond;
|
||||
currentMode = btn.dataset.mode;
|
||||
modeBtns.forEach(b => b.classList.remove('active'));
|
||||
btn.classList.add('active');
|
||||
osPickerExpanded = false; // Reset when switching modes
|
||||
updateVisibility();
|
||||
});
|
||||
});
|
||||
@ -505,10 +461,8 @@ const duration2 = (row2.length / 2 * pixelsPerItem) / pixelsPerSecond;
|
||||
return currentBeta
|
||||
? "curl -fsSL https://openclaw.ai/install.sh | bash -s -- --beta"
|
||||
: "curl -fsSL https://openclaw.ai/install.sh | bash";
|
||||
} else if (currentWinShell === 'cmd') {
|
||||
return currentBeta ? windowsCmdBetaCmd : windowsCmdCmd;
|
||||
} else {
|
||||
return currentBeta ? windowsPsBetaCmd : windowsPsCmd;
|
||||
return currentBeta ? windowsBetaCmd : windowsCmd;
|
||||
}
|
||||
},
|
||||
'install': () => {
|
||||
@ -516,10 +470,12 @@ const duration2 = (row2.length / 2 * pixelsPerItem) / pixelsPerSecond;
|
||||
return currentPm === 'npm' ? `npm i -g openclaw${betaSuffix}` : `pnpm add -g openclaw${betaSuffix}`;
|
||||
},
|
||||
'onboard': () => 'openclaw onboard',
|
||||
'hackable-installer': () => "curl -fsSL https://openclaw.ai/install.sh | bash -s -- --install-method git",
|
||||
'hackable-installer': () => currentOs === 'windows'
|
||||
? windowsGitCmd
|
||||
: "curl -fsSL https://openclaw.ai/install.sh | bash -s -- --install-method git",
|
||||
'clone': () => 'git clone https://github.com/openclaw/openclaw.git',
|
||||
'build': () => 'cd openclaw && pnpm install && pnpm run build',
|
||||
'hackable-onboard': () => 'pnpm run openclaw onboard',
|
||||
'build': () => 'cd openclaw && corepack enable && pnpm install',
|
||||
'hackable-onboard': () => 'pnpm openclaw onboard',
|
||||
};
|
||||
|
||||
document.querySelectorAll('.copy-line-btn').forEach(btn => {
|
||||
@ -667,34 +623,56 @@ const duration2 = (row2.length / 2 * pixelsPerItem) / pixelsPerSecond;
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Community Builds -->
|
||||
<section class="builds-section">
|
||||
<SectionHeader
|
||||
title="What People Are Building"
|
||||
link={{ href: "https://docs.openclaw.ai/start/showcase", text: "View docs showcase" }}
|
||||
/>
|
||||
<div class="builds-grid">
|
||||
{featuredBuilds.map((build) => (
|
||||
<a href={build.href} target="_blank" rel="noopener" class="build-card">
|
||||
<div class="build-card-header">
|
||||
<span class="build-source">{build.source}</span>
|
||||
<span class="build-open">Open →</span>
|
||||
</div>
|
||||
<h3 class="build-title">{build.title}</h3>
|
||||
<p class="build-summary">{build.summary}</p>
|
||||
<div class="build-tags">
|
||||
{build.tags.map((tag: string) => (
|
||||
<span class="build-tag">{tag}</span>
|
||||
))}
|
||||
</div>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
<div class="build-links">
|
||||
<a href="/showcase" class="build-link">Community chatter →</a>
|
||||
<span class="link-separator">·</span>
|
||||
<a href="https://docs.openclaw.ai/start/showcase" class="build-link" target="_blank" rel="noopener">More project examples →</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Press Section -->
|
||||
<section class="press-section">
|
||||
<SectionHeader title="Featured In" />
|
||||
<SectionHeader title="Featured In" link={{ href: "/press", text: "View all" }} />
|
||||
<div class="press-grid">
|
||||
<a href="https://www.macstories.net/stories/clawdbot-showed-me-what-the-future-of-personal-ai-assistants-looks-like/" target="_blank" rel="noopener" class="press-card press-featured">
|
||||
<div class="press-logo">
|
||||
<svg viewBox="0 0 24 24" fill="currentColor" class="press-icon">
|
||||
<path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.81-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z"/>
|
||||
</svg>
|
||||
<span class="press-name">MacStories</span>
|
||||
</div>
|
||||
<blockquote class="press-quote">
|
||||
"OpenClaw Showed Me What the Future of Personal AI Assistants Looks Like"
|
||||
</blockquote>
|
||||
<span class="press-author">Federico Viticci</span>
|
||||
</a>
|
||||
<a href="https://www.starryhope.com/minipcs/clawdbot-mac-mini-ai-agent-trend/" target="_blank" rel="noopener" class="press-card">
|
||||
<div class="press-logo">
|
||||
<svg viewBox="0 0 24 24" fill="currentColor" class="press-icon">
|
||||
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/>
|
||||
</svg>
|
||||
<span class="press-name">StarryHope</span>
|
||||
</div>
|
||||
<blockquote class="press-quote">
|
||||
"The Lobster Takeover: Why Developers Are Buying Mac Minis to Run Their Own AI Agents"
|
||||
</blockquote>
|
||||
<span class="press-author">Jim Mendenhall</span>
|
||||
</a>
|
||||
{featuredPress.map((article) => (
|
||||
<a
|
||||
href={article.url}
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
class:list={['press-card', article.featured && 'press-featured']}
|
||||
>
|
||||
<div class="press-meta">
|
||||
<span class="press-name">{article.outlet}</span>
|
||||
<span class="press-date">{article.displayDate}</span>
|
||||
</div>
|
||||
<h3 class="press-title">{article.title}</h3>
|
||||
<p class="press-summary">{article.summary}</p>
|
||||
<span class="press-author">{article.author}</span>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@ -771,17 +749,25 @@ const duration2 = (row2.length / 2 * pixelsPerItem) / pixelsPerSecond;
|
||||
</h2>
|
||||
<div class="sponsors-grid">
|
||||
<a href="https://openai.com" target="_blank" rel="noopener" class="sponsor-card">
|
||||
<img src="/sponsors/openai.svg" alt="OpenAI" class="sponsor-logo sponsor-logo-openai" loading="lazy" />
|
||||
<img src="/sponsors/openai.svg" alt="OpenAI" class="sponsor-logo sponsor-logo-openai sponsor-logo-invert-on-light" loading="lazy" />
|
||||
</a>
|
||||
<a href="https://github.com" target="_blank" rel="noopener" class="sponsor-card">
|
||||
<img src="/sponsors/github.svg" alt="GitHub" class="sponsor-logo sponsor-logo-github sponsor-logo-dark-only" loading="lazy" />
|
||||
<img src="/sponsors/github-light.svg" alt="GitHub" class="sponsor-logo sponsor-logo-github sponsor-logo-light-only" loading="lazy" />
|
||||
</a>
|
||||
<a href="https://www.nvidia.com" target="_blank" rel="noopener" class="sponsor-card">
|
||||
<img src="/sponsors/nvidia-dark.svg" alt="NVIDIA" class="sponsor-logo sponsor-logo-nvidia sponsor-logo-dark-only" loading="lazy" />
|
||||
<img src="/sponsors/nvidia-light.svg" alt="NVIDIA" class="sponsor-logo sponsor-logo-nvidia sponsor-logo-light-only" loading="lazy" />
|
||||
</a>
|
||||
<a href="https://vercel.com" target="_blank" rel="noopener" class="sponsor-card">
|
||||
<img src="/sponsors/vercel-dark.svg" alt="Vercel" class="sponsor-logo sponsor-logo-vercel sponsor-logo-dark-only" loading="lazy" />
|
||||
<img src="/sponsors/vercel-light.svg" alt="Vercel" class="sponsor-logo sponsor-logo-vercel sponsor-logo-light-only" loading="lazy" />
|
||||
</a>
|
||||
<a href="https://blacksmith.sh" target="_blank" rel="noopener" class="sponsor-card">
|
||||
<img src="/sponsors/blacksmith.svg" alt="Blacksmith" class="sponsor-logo sponsor-logo-blacksmith" loading="lazy" />
|
||||
<img src="/sponsors/blacksmith.svg" alt="Blacksmith" class="sponsor-logo sponsor-logo-blacksmith sponsor-logo-invert-on-light" loading="lazy" />
|
||||
</a>
|
||||
<a href="https://www.convex.dev" target="_blank" rel="noopener" class="sponsor-card">
|
||||
<img src="/sponsors/convex.svg" alt="Convex" class="sponsor-logo sponsor-logo-convex" loading="lazy" />
|
||||
<img src="/sponsors/convex.svg" alt="Convex" class="sponsor-logo sponsor-logo-convex sponsor-logo-invert-on-light" loading="lazy" />
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
@ -793,6 +779,8 @@ const duration2 = (row2.length / 2 * pixelsPerItem) / pixelsPerSecond;
|
||||
<span class="footer-separator">·</span>
|
||||
<a href="/showcase">Showcase</a>
|
||||
<span class="footer-separator">·</span>
|
||||
<a href="/press">Press</a>
|
||||
<span class="footer-separator">·</span>
|
||||
<a href="/shoutouts">Shoutouts</a>
|
||||
<span class="footer-separator">·</span>
|
||||
<a href="/integrations">Integrations</a>
|
||||
@ -800,7 +788,7 @@ const duration2 = (row2.length / 2 * pixelsPerItem) / pixelsPerSecond;
|
||||
<a href="https://trust.openclaw.ai/">Trust</a>
|
||||
</nav>
|
||||
<p>Built by <a href="https://molty.me" target="_blank" rel="noopener">Molty</a> 🦞, a space lobster AI with a <a href="https://soul.md" target="_blank" rel="noopener">soul</a>, by <a href="https://steipete.me" target="_blank" rel="noopener">Peter Steinberger</a> & <a href="https://github.com/openclaw/openclaw#community" target="_blank" rel="noopener">community</a>.</p>
|
||||
<p class="disclaimer">Formerly known as Clawdbot and Moltbot. Independent project, not affiliated with Anthropic.</p>
|
||||
<p class="disclaimer">Independent project, not affiliated with Anthropic.</p>
|
||||
</footer>
|
||||
</main>
|
||||
</Layout>
|
||||
@ -1320,13 +1308,119 @@ const duration2 = (row2.length / 2 * pixelsPerItem) / pixelsPerSecond;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* Community Builds */
|
||||
.builds-section {
|
||||
margin-bottom: 56px;
|
||||
animation: fadeInUp 0.8s ease-out 0.62s both;
|
||||
}
|
||||
|
||||
.builds-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 20px;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.builds-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.build-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
padding: 22px;
|
||||
border-radius: 18px;
|
||||
border: 1px solid var(--border-subtle);
|
||||
background: var(--surface-card-strong);
|
||||
backdrop-filter: blur(12px);
|
||||
text-decoration: none;
|
||||
color: var(--text-primary);
|
||||
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.build-card:hover {
|
||||
transform: translateY(-4px);
|
||||
border-color: var(--cyan-bright);
|
||||
box-shadow: 0 12px 40px var(--shadow-cyan-soft);
|
||||
}
|
||||
|
||||
.build-card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.build-source,
|
||||
.build-tag {
|
||||
border: 1px solid var(--border-subtle);
|
||||
background: var(--surface-overlay);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.build-source {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-height: 28px;
|
||||
padding: 0 10px;
|
||||
border-radius: 999px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.build-open,
|
||||
.build-link {
|
||||
color: var(--coral-bright);
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.build-title {
|
||||
font-family: var(--font-display);
|
||||
font-size: 1.15rem;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.build-summary {
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.96rem;
|
||||
line-height: 1.65;
|
||||
}
|
||||
|
||||
.build-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.build-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-height: 28px;
|
||||
padding: 0 10px;
|
||||
border-radius: 999px;
|
||||
font-size: 0.78rem;
|
||||
}
|
||||
|
||||
.build-links {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
/* Press Section */
|
||||
.press-section {
|
||||
margin-bottom: 56px;
|
||||
animation: fadeInUp 0.8s ease-out 0.65s both;
|
||||
animation: fadeInUp 0.8s ease-out 0.68s both;
|
||||
}
|
||||
|
||||
|
||||
.press-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
@ -1342,9 +1436,8 @@ const duration2 = (row2.length / 2 * pixelsPerItem) / pixelsPerSecond;
|
||||
.press-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
padding: 24px 28px;
|
||||
gap: 14px;
|
||||
padding: 24px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-subtle);
|
||||
background: var(--surface-card-strong);
|
||||
@ -1352,7 +1445,6 @@ const duration2 = (row2.length / 2 * pixelsPerItem) / pixelsPerSecond;
|
||||
text-decoration: none;
|
||||
color: var(--text-primary);
|
||||
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.press-card:hover {
|
||||
@ -1361,39 +1453,44 @@ const duration2 = (row2.length / 2 * pixelsPerItem) / pixelsPerSecond;
|
||||
box-shadow: 0 12px 40px var(--shadow-coral-soft);
|
||||
}
|
||||
|
||||
.press-logo {
|
||||
.press-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.press-icon {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
color: var(--text-muted);
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.press-name {
|
||||
font-family: var(--font-display);
|
||||
font-size: 1.1rem;
|
||||
font-size: 0.82rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-secondary);
|
||||
letter-spacing: 0.02em;
|
||||
color: var(--coral-bright);
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.press-quote {
|
||||
.press-date {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
|
||||
.press-title {
|
||||
font-family: var(--font-display);
|
||||
font-size: 1rem;
|
||||
font-size: 1.15rem;
|
||||
font-weight: 500;
|
||||
line-height: 1.5;
|
||||
line-height: 1.35;
|
||||
color: var(--text-primary);
|
||||
margin: 0;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.press-summary {
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.65;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.press-author {
|
||||
font-size: 0.9rem;
|
||||
color: var(--coral-bright);
|
||||
font-size: 0.88rem;
|
||||
color: var(--text-muted);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
@ -1506,8 +1603,7 @@ const duration2 = (row2.length / 2 * pixelsPerItem) / pixelsPerSecond;
|
||||
.mode-switch,
|
||||
.pm-switch,
|
||||
.hackable-switch,
|
||||
.os-switch,
|
||||
.win-shell-switch {
|
||||
.os-switch {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
background: var(--surface-overlay);
|
||||
@ -1517,20 +1613,14 @@ const duration2 = (row2.length / 2 * pixelsPerItem) / pixelsPerSecond;
|
||||
|
||||
.pm-switch,
|
||||
.hackable-switch,
|
||||
.os-switch,
|
||||
.win-shell-switch {
|
||||
.os-switch {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.win-shell-switch {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.mode-btn,
|
||||
.pm-btn,
|
||||
.hackable-btn,
|
||||
.os-btn,
|
||||
.win-shell-btn {
|
||||
.os-btn {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.7rem;
|
||||
padding: 4px 10px;
|
||||
@ -1545,15 +1635,13 @@ const duration2 = (row2.length / 2 * pixelsPerItem) / pixelsPerSecond;
|
||||
.mode-btn:hover,
|
||||
.pm-btn:hover,
|
||||
.hackable-btn:hover,
|
||||
.os-btn:hover,
|
||||
.win-shell-btn:hover {
|
||||
.os-btn:hover {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.pm-btn.active,
|
||||
.hackable-btn.active,
|
||||
.os-btn.active,
|
||||
.win-shell-btn.active {
|
||||
.os-btn.active {
|
||||
background: var(--coral-bright);
|
||||
color: var(--bg-deep);
|
||||
font-weight: 600;
|
||||
@ -1646,38 +1734,6 @@ const duration2 = (row2.length / 2 * pixelsPerItem) / pixelsPerSecond;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* OS Indicator */
|
||||
.os-indicator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-left: auto;
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.os-detected {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.os-change-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--coral-bright);
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.7rem;
|
||||
cursor: pointer;
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
.os-change-btn:hover {
|
||||
background: var(--surface-coral-soft);
|
||||
color: var(--cyan-bright);
|
||||
}
|
||||
|
||||
/* macOS App Download Section */
|
||||
.macos-app-content {
|
||||
display: flex;
|
||||
@ -1783,7 +1839,7 @@ const duration2 = (row2.length / 2 * pixelsPerItem) / pixelsPerSecond;
|
||||
transition: opacity 0.25s ease;
|
||||
}
|
||||
|
||||
:global(html[data-theme='light']) .sponsor-logo:not(.sponsor-logo-light-only) {
|
||||
:global(html[data-theme='light']) .sponsor-logo-invert-on-light {
|
||||
filter: invert(1);
|
||||
opacity: 0.8;
|
||||
}
|
||||
@ -1796,6 +1852,14 @@ const duration2 = (row2.length / 2 * pixelsPerItem) / pixelsPerSecond;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.sponsor-logo-github {
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.sponsor-logo-nvidia {
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.sponsor-logo-blacksmith {
|
||||
height: 32px;
|
||||
}
|
||||
@ -1837,6 +1901,14 @@ const duration2 = (row2.length / 2 * pixelsPerItem) / pixelsPerSecond;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.sponsor-logo-github {
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
.sponsor-logo-nvidia {
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
.sponsor-logo-blacksmith {
|
||||
height: 24px;
|
||||
}
|
||||
@ -2140,9 +2212,7 @@ const duration2 = (row2.length / 2 * pixelsPerItem) / pixelsPerSecond;
|
||||
}
|
||||
|
||||
/* Contextual switches appear first (after dots), right-aligned */
|
||||
.os-indicator,
|
||||
.os-switch,
|
||||
.win-shell-switch,
|
||||
.pm-switch,
|
||||
.hackable-switch,
|
||||
.beta-switch,
|
||||
@ -2151,10 +2221,6 @@ const duration2 = (row2.length / 2 * pixelsPerItem) / pixelsPerSecond;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.win-shell-switch {
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
/* Mode tabs appear last */
|
||||
.mode-switch {
|
||||
order: 2;
|
||||
@ -2169,8 +2235,7 @@ const duration2 = (row2.length / 2 * pixelsPerItem) / pixelsPerSecond;
|
||||
}
|
||||
|
||||
.os-btn,
|
||||
.pm-btn,
|
||||
.win-shell-btn {
|
||||
.pm-btn {
|
||||
padding: 4px 8px;
|
||||
font-size: 0.65rem;
|
||||
}
|
||||
|
||||
@ -9,7 +9,7 @@ import {
|
||||
siPhilipshue, siHomeassistant,
|
||||
siGooglechrome, siGmail, si1password,
|
||||
siX, siVercel,
|
||||
siLinux, siAndroid, siMacos, siIos
|
||||
siLinux, siAndroid, siMacos, siIos, siQq
|
||||
} from 'simple-icons';
|
||||
|
||||
// Helper to create SVG from simple-icons
|
||||
@ -76,7 +76,7 @@ const chatProviders = [
|
||||
{ name: 'Signal', icon: siIcon(siSignal), color: '#3A76F0', desc: 'Privacy-focused via signal-cli', docs: 'https://docs.openclaw.ai/channels/signal' },
|
||||
{ name: 'iMessage', icon: siIcon(siApple), color: '#007AFF', desc: 'iMessage via imsg (AppleScript bridge)', docs: 'https://github.com/steipete/imsg' },
|
||||
{ name: 'iMessage', icon: bluebubblesIcon, color: '#3B82F6', desc: 'iMessage via BlueBubbles server', docs: 'https://docs.openclaw.ai/channels/bluebubbles' },
|
||||
{ name: 'Microsoft Teams', icon: 'lucide:users', color: '#6264A7', desc: 'Enterprise support', docs: 'https://docs.openclaw.ai/msteams' },
|
||||
{ name: 'Microsoft Teams', icon: 'lucide:users', color: '#6264A7', desc: 'Enterprise support', docs: 'https://docs.openclaw.ai/channels/msteams' },
|
||||
{ name: 'Nextcloud Talk', icon: siIcon(siNextcloud), color: '#0082C9', desc: 'Self-hosted Nextcloud chat', docs: 'https://docs.openclaw.ai/channels/nextcloud-talk' },
|
||||
{ name: 'Matrix', icon: siIcon(siMatrix), color: 'currentColor', desc: 'Matrix protocol', docs: 'https://docs.openclaw.ai/channels/matrix' },
|
||||
{ name: 'Nostr', icon: 'lucide:message-circle', color: '#8F2CFF', desc: 'Decentralized DMs via NIP-04', docs: 'https://docs.openclaw.ai/channels/nostr' },
|
||||
@ -84,6 +84,7 @@ const chatProviders = [
|
||||
{ name: 'Zalo', icon: siIcon(siZalo), color: '#0068FF', desc: 'Zalo Bot API', docs: 'https://docs.openclaw.ai/channels/zalo' },
|
||||
{ name: 'Zalo Personal', icon: siIcon(siZalo), color: '#0068FF', desc: 'Personal account via QR login', docs: 'https://docs.openclaw.ai/channels/zalouser' },
|
||||
{ name: 'WebChat', icon: 'lucide:globe', color: '#00E5CC', desc: 'Browser-based UI', docs: 'https://docs.openclaw.ai/webchat' },
|
||||
{ name: 'QQ', icon: siIcon(siQq), color: '#1EBAFC', desc: 'QQ Bot Official API', docs: 'https://docs.openclaw.ai/channels/qqbot' },
|
||||
];
|
||||
|
||||
const modelProviders = [
|
||||
@ -114,11 +115,11 @@ const productivityApps = [
|
||||
{ name: 'Apple Notes', icon: 'lucide:sticky-note', color: '#FFCC00', desc: 'Native macOS/iOS notes', docs: 'https://clawhub.ai/skills/apple-notes' },
|
||||
{ name: 'Apple Reminders', icon: 'lucide:check-square', color: '#FF9500', desc: 'Task management', docs: 'https://clawhub.ai/skills/apple-reminders' },
|
||||
{ name: 'Things 3', icon: 'lucide:list-todo', color: '#4A90D9', desc: 'GTD task manager', docs: 'https://clawhub.ai/skills/things-mac' },
|
||||
{ name: 'Notion', icon: siIcon(siNotion), color: '#FFFFFF', desc: 'Workspace & databases', docs: 'https://clawhub.ai/skills' },
|
||||
{ name: 'Notion', icon: siIcon(siNotion), color: '#FFFFFF', desc: 'Workspace & databases', docs: 'https://clawhub.ai/skills/notion' },
|
||||
{ name: 'Obsidian', icon: siIcon(siObsidian), color: '#7C3AED', desc: 'Knowledge graph notes', docs: 'https://clawhub.ai/skills/obsidian' },
|
||||
{ name: 'Bear Notes', icon: 'lucide:pen-tool', color: '#DD4C4F', desc: 'Markdown notes', docs: 'https://clawhub.ai/skills' },
|
||||
{ name: 'Bear Notes', icon: 'lucide:pen-tool', color: '#DD4C4F', desc: 'Markdown notes', docs: 'https://clawhub.ai/skills/bear-notes' },
|
||||
{ name: 'Trello', icon: siIcon(siTrello), color: '#0079BF', desc: 'Kanban boards', docs: 'https://clawhub.ai/skills/trello' },
|
||||
{ name: 'GitHub', icon: siIcon(siGithub), color: '#FFFFFF', desc: 'Code, issues, PRs', docs: 'https://clawhub.ai/skills' },
|
||||
{ name: 'GitHub', icon: siIcon(siGithub), color: '#FFFFFF', desc: 'Code, issues, PRs', docs: 'https://clawhub.ai/skills/github' },
|
||||
];
|
||||
|
||||
const musicAudio = [
|
||||
@ -145,10 +146,10 @@ const tools = [
|
||||
];
|
||||
|
||||
const mediaCreative = [
|
||||
{ name: 'Image Gen', icon: 'lucide:image', color: '#E91E63', desc: 'AI image generation', docs: 'https://clawhub.ai/skills' },
|
||||
{ name: 'Image Gen', icon: 'lucide:image', color: '#E91E63', desc: 'AI image generation', docs: 'https://clawhub.ai/skills/image-gen' },
|
||||
{ name: 'GIF Search', icon: 'lucide:search', color: '#00DCDC', desc: 'Find the perfect GIF', docs: 'https://clawhub.ai/skills/gifgrep' },
|
||||
{ name: 'Peekaboo', icon: 'lucide:eye', color: '#FF6B6B', desc: 'Screen capture & control', docs: 'https://clawhub.ai/skills/peekaboo' },
|
||||
{ name: 'Camera', icon: 'lucide:camera', color: '#607D8B', desc: 'Photo/video capture', docs: 'https://clawhub.ai/skills' },
|
||||
{ name: 'Camera', icon: 'lucide:camera', color: '#607D8B', desc: 'Photo/video capture', docs: 'https://clawhub.ai/skills/camera' },
|
||||
];
|
||||
|
||||
const socialComms = [
|
||||
@ -378,6 +379,8 @@ const showcase = [
|
||||
<span class="footer-separator">·</span>
|
||||
<a href="/showcase">Showcase</a>
|
||||
<span class="footer-separator">·</span>
|
||||
<a href="/press">Press</a>
|
||||
<span class="footer-separator">·</span>
|
||||
<a href="/shoutouts">Shoutouts</a>
|
||||
<span class="footer-separator">·</span>
|
||||
<a href="https://trust.openclaw.ai/">Trust</a>
|
||||
|
||||
278
src/pages/press.astro
Normal file
278
src/pages/press.astro
Normal file
@ -0,0 +1,278 @@
|
||||
---
|
||||
import Layout from '../layouts/Layout.astro';
|
||||
import pressArticles from '../data/press.json';
|
||||
---
|
||||
|
||||
<Layout
|
||||
title="Press — OpenClaw"
|
||||
description="Coverage of OpenClaw across technology, business, and culture."
|
||||
canonicalUrl="https://openclaw.ai/press"
|
||||
>
|
||||
<div class="stars"></div>
|
||||
<div class="nebula"></div>
|
||||
|
||||
<main class="container">
|
||||
<header class="header">
|
||||
<a href="/" class="back-link">← Back to home</a>
|
||||
<h1 class="title">
|
||||
<span class="claw-accent">⟩</span> Press
|
||||
</h1>
|
||||
<p class="subtitle">
|
||||
Coverage of OpenClaw across product, business, culture, and the broader personal-agent wave.
|
||||
</p>
|
||||
<p class="note">Coverage selected for signal and relevance across product, business, and community momentum.</p>
|
||||
</header>
|
||||
|
||||
<div class="press-grid">
|
||||
{pressArticles.map((article) => (
|
||||
<a
|
||||
href={article.url}
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
class:list={['press-card', article.featured && 'press-featured']}
|
||||
>
|
||||
<div class="press-meta">
|
||||
<span class="press-outlet">{article.outlet}</span>
|
||||
<span class="press-date">{article.displayDate}</span>
|
||||
</div>
|
||||
<h2 class="press-title">{article.title}</h2>
|
||||
<p class="press-summary">{article.summary}</p>
|
||||
<span class="press-author">{article.author}</span>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<footer class="footer">
|
||||
<nav class="footer-nav">
|
||||
<a href="/">Home</a>
|
||||
<span class="footer-separator">·</span>
|
||||
<a href="/blog">Blog</a>
|
||||
<span class="footer-separator">·</span>
|
||||
<a href="/showcase">Showcase</a>
|
||||
<span class="footer-separator">·</span>
|
||||
<a href="/shoutouts">Shoutouts</a>
|
||||
<span class="footer-separator">·</span>
|
||||
<a href="/integrations">Integrations</a>
|
||||
</nav>
|
||||
<p>Built by <a href="https://molty.me" target="_blank" rel="noopener">Molty</a> 🦞</p>
|
||||
</footer>
|
||||
</main>
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
.stars {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background-image:
|
||||
radial-gradient(2px 2px at 20px 30px, rgba(255,255,255,0.8), transparent),
|
||||
radial-gradient(2px 2px at 40px 70px, rgba(255,255,255,0.5), transparent),
|
||||
radial-gradient(1px 1px at 90px 40px, rgba(255,255,255,0.6), transparent),
|
||||
radial-gradient(2px 2px at 130px 80px, rgba(255,255,255,0.4), transparent),
|
||||
radial-gradient(1px 1px at 160px 120px, rgba(255,255,255,0.7), transparent),
|
||||
radial-gradient(2px 2px at 200px 60px, rgba(0,229,204,0.6), transparent),
|
||||
radial-gradient(1px 1px at 250px 150px, rgba(255,255,255,0.5), transparent),
|
||||
radial-gradient(2px 2px at 300px 40px, rgba(255,77,77,0.4), transparent);
|
||||
background-size: 350px 200px;
|
||||
animation: twinkle 8s ease-in-out infinite alternate;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
@keyframes twinkle {
|
||||
0% { opacity: 0.4; }
|
||||
100% { opacity: 0.7; }
|
||||
}
|
||||
|
||||
.nebula {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background:
|
||||
radial-gradient(ellipse 80% 50% at 20% 20%, rgba(255, 77, 77, 0.12), transparent 50%),
|
||||
radial-gradient(ellipse 60% 60% at 80% 30%, rgba(0, 229, 204, 0.08), transparent 50%),
|
||||
radial-gradient(ellipse 90% 70% at 50% 90%, rgba(255, 77, 77, 0.06), transparent 50%);
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.container {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
max-width: 1100px;
|
||||
margin: 0 auto;
|
||||
padding: 40px 24px;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 48px;
|
||||
animation: fadeInUp 0.6s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.back-link {
|
||||
display: inline-block;
|
||||
margin-bottom: 24px;
|
||||
color: var(--text-muted);
|
||||
text-decoration: none;
|
||||
font-size: 0.9rem;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.back-link:hover {
|
||||
color: var(--coral-bright);
|
||||
}
|
||||
|
||||
.title {
|
||||
font-family: var(--font-display);
|
||||
font-size: clamp(2rem, 6vw, 3rem);
|
||||
font-weight: 700;
|
||||
margin-bottom: 12px;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.claw-accent {
|
||||
color: var(--coral-bright);
|
||||
}
|
||||
|
||||
.subtitle,
|
||||
.note {
|
||||
max-width: 700px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
color: var(--text-secondary);
|
||||
font-size: 1.08rem;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.note {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.press-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.press-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
padding: 24px;
|
||||
border-radius: 18px;
|
||||
border: 1px solid var(--border-subtle);
|
||||
background: var(--surface-card-strong);
|
||||
backdrop-filter: blur(12px);
|
||||
text-decoration: none;
|
||||
color: var(--text-primary);
|
||||
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.press-card:hover {
|
||||
transform: translateY(-4px);
|
||||
border-color: var(--coral-bright);
|
||||
box-shadow: 0 12px 40px var(--shadow-coral-soft);
|
||||
}
|
||||
|
||||
.press-featured {
|
||||
border-color: var(--border-accent);
|
||||
background: var(--press-featured-gradient);
|
||||
}
|
||||
|
||||
.press-meta {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.press-outlet {
|
||||
color: var(--coral-bright);
|
||||
font-size: 0.82rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.press-date {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
|
||||
.press-title {
|
||||
font-family: var(--font-display);
|
||||
font-size: 1.2rem;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.press-summary {
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.65;
|
||||
font-size: 0.96rem;
|
||||
}
|
||||
|
||||
.press-author {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.88rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: auto;
|
||||
padding-top: 48px;
|
||||
text-align: center;
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.footer-nav {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
column-gap: 8px;
|
||||
row-gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.footer-separator {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.footer a {
|
||||
color: var(--coral-bright);
|
||||
text-decoration: none;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.footer a:hover {
|
||||
color: var(--cyan-bright);
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.container {
|
||||
padding: 24px 16px;
|
||||
}
|
||||
|
||||
.press-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -12,7 +12,7 @@ export async function GET(context) {
|
||||
title: post.data.title,
|
||||
description: post.data.description,
|
||||
pubDate: post.data.date,
|
||||
link: `/blog/${post.slug}`,
|
||||
link: `/blog/${post.id}`,
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
@ -46,6 +46,8 @@ const allTestimonials = [...testimonials, ...extraTestimonials];
|
||||
<span class="footer-separator">·</span>
|
||||
<a href="/showcase">Showcase</a>
|
||||
<span class="footer-separator">·</span>
|
||||
<a href="/press">Press</a>
|
||||
<span class="footer-separator">·</span>
|
||||
<a href="/integrations">Integrations</a>
|
||||
<span class="footer-separator">·</span>
|
||||
<a href="https://trust.openclaw.ai/">Trust</a>
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
---
|
||||
import Layout from '../layouts/Layout.astro';
|
||||
import showcaseData from '../data/showcase.json';
|
||||
import featuredBuilds from '../data/community-builds.json';
|
||||
|
||||
// Use JSON order directly.
|
||||
const sortedShowcase = showcaseData;
|
||||
const featuredIds = new Set(featuredBuilds.map((item) => item.id));
|
||||
const sortedShowcase = showcaseData.filter((item) => !featuredIds.has(item.id));
|
||||
|
||||
const categoryLabels: Record<string, string> = {
|
||||
'power-user': '🚀 Power User',
|
||||
@ -27,9 +28,43 @@ const categoryLabels: Record<string, string> = {
|
||||
<h1 class="title">
|
||||
<span class="claw-accent">⟩</span> What People Are Building
|
||||
</h1>
|
||||
<p class="subtitle">Real projects, real automation, real magic.</p>
|
||||
<p class="subtitle">The strongest OpenClaw builds we could verify in public: X threads, shipped workflows, and real automation.</p>
|
||||
</header>
|
||||
|
||||
<section class="featured-section">
|
||||
<div class="featured-header">
|
||||
<h2 class="featured-title">Featured Builds</h2>
|
||||
<p class="featured-copy">
|
||||
Handpicked examples that show range: personal ops, coding, family workflows, and “I cannot believe this is running from chat.”
|
||||
</p>
|
||||
</div>
|
||||
<div class="featured-grid">
|
||||
{featuredBuilds.map((build) => (
|
||||
<a href={build.href} target="_blank" rel="noopener" class="featured-card">
|
||||
<div class="featured-topline">
|
||||
<span class="featured-source">{build.source}</span>
|
||||
<span class="featured-likes">❤️ {build.likes}</span>
|
||||
</div>
|
||||
<h3 class="featured-card-title">{build.title}</h3>
|
||||
<p class="featured-summary">{build.summary}</p>
|
||||
<div class="featured-meta">
|
||||
<span class="featured-author">@{build.author}</span>
|
||||
<div class="featured-tags">
|
||||
{build.tags.map((tag: string) => (
|
||||
<span class="featured-tag">{tag}</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="grid-header">
|
||||
<h2 class="grid-title">More From the Community</h2>
|
||||
<p class="grid-copy">A broader stream of OpenClaw projects, experiments, and production workflows.</p>
|
||||
</div>
|
||||
|
||||
<div class="showcase-grid">
|
||||
{sortedShowcase.map((item) => (
|
||||
<a href={`https://x.com/${item.author}/status/${item.id}`} target="_blank" rel="noopener" class="showcase-card">
|
||||
@ -79,6 +114,8 @@ const categoryLabels: Record<string, string> = {
|
||||
<span class="footer-separator">·</span>
|
||||
<a href="/blog">Blog</a>
|
||||
<span class="footer-separator">·</span>
|
||||
<a href="/press">Press</a>
|
||||
<span class="footer-separator">·</span>
|
||||
<a href="/shoutouts">Shoutouts</a>
|
||||
<span class="footer-separator">·</span>
|
||||
<a href="/integrations">Integrations</a>
|
||||
@ -175,6 +212,122 @@ const categoryLabels: Record<string, string> = {
|
||||
.subtitle {
|
||||
color: var(--text-secondary);
|
||||
font-size: 1.1rem;
|
||||
max-width: 780px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.featured-section {
|
||||
margin-bottom: 48px;
|
||||
}
|
||||
|
||||
.featured-header {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
margin-bottom: 22px;
|
||||
}
|
||||
|
||||
.featured-title,
|
||||
.grid-title {
|
||||
font-family: var(--font-display);
|
||||
font-size: 1.45rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.featured-copy,
|
||||
.grid-copy {
|
||||
color: var(--text-secondary);
|
||||
max-width: 760px;
|
||||
}
|
||||
|
||||
.featured-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.featured-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
padding: 22px;
|
||||
border-radius: 18px;
|
||||
border: 1px solid var(--border-subtle);
|
||||
background:
|
||||
linear-gradient(135deg, rgba(255, 77, 77, 0.06), transparent 40%),
|
||||
var(--surface-card-strong);
|
||||
backdrop-filter: blur(10px);
|
||||
text-decoration: none;
|
||||
color: var(--text-primary);
|
||||
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.featured-card:hover {
|
||||
transform: translateY(-4px);
|
||||
border-color: var(--coral-bright);
|
||||
box-shadow: 0 16px 48px var(--shadow-coral-soft);
|
||||
}
|
||||
|
||||
.featured-topline,
|
||||
.featured-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.featured-source,
|
||||
.featured-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-height: 28px;
|
||||
padding: 0 10px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid var(--border-subtle);
|
||||
background: var(--surface-overlay);
|
||||
color: var(--text-muted);
|
||||
font-size: 0.76rem;
|
||||
}
|
||||
|
||||
.featured-source {
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.featured-likes {
|
||||
font-size: 0.84rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.featured-card-title {
|
||||
font-family: var(--font-display);
|
||||
font-size: 1.2rem;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.featured-summary {
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.65;
|
||||
}
|
||||
|
||||
.featured-author {
|
||||
color: var(--coral-bright);
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.featured-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.grid-header {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.showcase-grid {
|
||||
@ -409,6 +562,19 @@ const categoryLabels: Record<string, string> = {
|
||||
padding: 24px 16px;
|
||||
}
|
||||
|
||||
.featured-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.featured-meta {
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.featured-tags {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.showcase-grid {
|
||||
column-width: auto;
|
||||
column-count: 1;
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
---
|
||||
import TrustTopbar from '../../components/TrustTopbar.astro';
|
||||
|
||||
const isSubdomain = Astro.url.hostname === 'trust.openclaw.ai';
|
||||
const trustHref = isSubdomain ? '/' : '/trust';
|
||||
const threatModelHref = isSubdomain ? '/threatmodel' : '/trust/threatmodel';
|
||||
@ -59,94 +61,6 @@ const langLinks = {
|
||||
a { color: var(--gray-500); text-decoration: none; }
|
||||
a:hover { color: var(--gray-900); }
|
||||
|
||||
/* Topbar */
|
||||
.topbar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
background: var(--bg);
|
||||
border-bottom: 1px solid var(--gray-200);
|
||||
}
|
||||
|
||||
.topbar-inner {
|
||||
max-width: 720px;
|
||||
margin: 0 auto;
|
||||
padding: 0 24px;
|
||||
height: 52px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.topbar-brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-decoration: none;
|
||||
color: var(--gray-900);
|
||||
}
|
||||
|
||||
.topbar-brand:hover { color: var(--gray-900); }
|
||||
|
||||
.topbar-brand img {
|
||||
height: 28px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.topbar-nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.topbar-link {
|
||||
padding: 6px 14px;
|
||||
border-radius: 8px;
|
||||
font-family: var(--heading);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--gray-500);
|
||||
transition: color 0.15s, background 0.15s;
|
||||
}
|
||||
|
||||
.topbar-link:hover {
|
||||
color: var(--gray-900);
|
||||
background: var(--gray-50);
|
||||
}
|
||||
|
||||
.topbar-link.active {
|
||||
color: var(--primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.lang-switcher {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
margin-left: 8px;
|
||||
padding-left: 10px;
|
||||
border-left: 1px solid var(--gray-200);
|
||||
}
|
||||
|
||||
.lang-link {
|
||||
padding: 4px 8px;
|
||||
border-radius: 6px;
|
||||
font-family: var(--heading);
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: var(--gray-400);
|
||||
transition: color 0.15s, background 0.15s;
|
||||
}
|
||||
|
||||
.lang-link:hover {
|
||||
color: var(--gray-900);
|
||||
background: var(--gray-50);
|
||||
}
|
||||
|
||||
.lang-link.active {
|
||||
color: var(--primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 720px;
|
||||
margin: 0 auto;
|
||||
@ -476,29 +390,17 @@ const langLinks = {
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.container { padding: 24px 16px; }
|
||||
.topbar-inner { padding: 0 16px; }
|
||||
.article-content p:first-child { font-size: 1.1rem; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="topbar">
|
||||
<div class="topbar-inner">
|
||||
<a href="https://openclaw.ai" class="topbar-brand">
|
||||
<img src="/openclaw-logo-text-dark.png" alt="OpenClaw" />
|
||||
</a>
|
||||
<div class="topbar-nav">
|
||||
<a href={trustHref} class="topbar-link active">Trust</a>
|
||||
<a href={threatModelHref} class="topbar-link">Threat Model</a>
|
||||
<div class="lang-switcher">
|
||||
<a href={langLinks.en.trust} class="lang-link active">{langLinks.en.label}</a>
|
||||
<a href={langLinks['zh-cn'].trust} class="lang-link">{langLinks['zh-cn'].label}</a>
|
||||
<a href={langLinks.ko.trust} class="lang-link">{langLinks.ko.label}</a>
|
||||
<a href={langLinks.ja.trust} class="lang-link">{langLinks.ja.label}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<TrustTopbar
|
||||
activePage="trust"
|
||||
locale="en"
|
||||
labels={{ trust: 'Trust', threatModel: 'Threat Model' }}
|
||||
langLinks={langLinks}
|
||||
/>
|
||||
|
||||
<main class="container">
|
||||
<article class="article">
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
---
|
||||
import TrustTopbar from '../../../components/TrustTopbar.astro';
|
||||
import t from '../../../i18n/ja/trust.json';
|
||||
|
||||
const isSubdomain = Astro.url.hostname === 'trust.openclaw.ai';
|
||||
@ -61,101 +62,6 @@ const langLinks = {
|
||||
a { color: var(--gray-500); text-decoration: none; }
|
||||
a:hover { color: var(--gray-900); }
|
||||
|
||||
/* Topbar */
|
||||
.topbar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
background: var(--bg);
|
||||
border-bottom: 1px solid var(--gray-200);
|
||||
}
|
||||
|
||||
.topbar-inner {
|
||||
max-width: 720px;
|
||||
margin: 0 auto;
|
||||
padding: 0 24px;
|
||||
height: 52px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.topbar-brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-decoration: none;
|
||||
color: var(--gray-900);
|
||||
}
|
||||
|
||||
.topbar-brand:hover { color: var(--gray-900); }
|
||||
|
||||
.topbar-brand img {
|
||||
height: 28px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.topbar-nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.topbar-link {
|
||||
padding: 6px 14px;
|
||||
border-radius: 8px;
|
||||
font-family: var(--heading);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--gray-500);
|
||||
transition: color 0.15s, background 0.15s;
|
||||
}
|
||||
|
||||
.topbar-link:hover {
|
||||
color: var(--gray-900);
|
||||
background: var(--gray-50);
|
||||
}
|
||||
|
||||
.topbar-link.active {
|
||||
color: var(--primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.lang-sep {
|
||||
color: var(--gray-200);
|
||||
font-size: 13px;
|
||||
margin: 0 2px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.lang-switcher {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
margin-left: 8px;
|
||||
padding-left: 10px;
|
||||
border-left: 1px solid var(--gray-200);
|
||||
}
|
||||
|
||||
.lang-link {
|
||||
padding: 4px 8px;
|
||||
border-radius: 6px;
|
||||
font-family: var(--heading);
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: var(--gray-400);
|
||||
transition: color 0.15s, background 0.15s;
|
||||
}
|
||||
|
||||
.lang-link:hover {
|
||||
color: var(--gray-900);
|
||||
background: var(--gray-50);
|
||||
}
|
||||
|
||||
.lang-link.active {
|
||||
color: var(--primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 720px;
|
||||
margin: 0 auto;
|
||||
@ -485,29 +391,17 @@ const langLinks = {
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.container { padding: 24px 16px; }
|
||||
.topbar-inner { padding: 0 16px; }
|
||||
.article-content p:first-child { font-size: 1.1rem; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="topbar">
|
||||
<div class="topbar-inner">
|
||||
<a href="https://openclaw.ai" class="topbar-brand">
|
||||
<img src="/openclaw-logo-text-dark.png" alt="OpenClaw" />
|
||||
</a>
|
||||
<div class="topbar-nav">
|
||||
<a href={trustHref} class="topbar-link active">{t.nav.trust}</a>
|
||||
<a href={threatModelHref} class="topbar-link">{t.nav.threatModel}</a>
|
||||
<div class="lang-switcher">
|
||||
<a href={langLinks.en.trust} class="lang-link">{langLinks.en.label}</a>
|
||||
<a href={langLinks['zh-cn'].trust} class="lang-link">{langLinks['zh-cn'].label}</a>
|
||||
<a href={langLinks.ko.trust} class="lang-link">{langLinks.ko.label}</a>
|
||||
<a href={langLinks.ja.trust} class="lang-link active">{langLinks.ja.label}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<TrustTopbar
|
||||
activePage="trust"
|
||||
locale="ja"
|
||||
labels={{ trust: t.nav.trust, threatModel: t.nav.threatModel }}
|
||||
langLinks={langLinks}
|
||||
/>
|
||||
|
||||
<main class="container">
|
||||
<article class="article">
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
---
|
||||
import TrustTopbar from '../../../components/TrustTopbar.astro';
|
||||
import yaml from 'js-yaml';
|
||||
import threatsYaml from '../../../i18n/ja/threats.yaml?raw';
|
||||
import t from '../../../i18n/ja/threatmodel.json';
|
||||
@ -110,94 +111,6 @@ for (const b of trustBoundaries) {
|
||||
a { color: var(--gray-500); text-decoration: none; }
|
||||
a:hover { color: var(--gray-900); }
|
||||
|
||||
/* Topbar */
|
||||
.topbar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
background: var(--bg);
|
||||
border-bottom: 1px solid var(--gray-200);
|
||||
}
|
||||
|
||||
.topbar-inner {
|
||||
max-width: 1800px;
|
||||
margin: 0 auto;
|
||||
padding: 0 24px;
|
||||
height: 52px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.topbar-brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-decoration: none;
|
||||
color: var(--gray-900);
|
||||
}
|
||||
|
||||
.topbar-brand:hover { color: var(--gray-900); }
|
||||
|
||||
.topbar-brand img {
|
||||
height: 28px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.topbar-nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.topbar-link {
|
||||
padding: 6px 14px;
|
||||
border-radius: 8px;
|
||||
font-family: var(--heading);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--gray-500);
|
||||
transition: color 0.15s, background 0.15s;
|
||||
}
|
||||
|
||||
.topbar-link:hover {
|
||||
color: var(--gray-900);
|
||||
background: var(--gray-50);
|
||||
}
|
||||
|
||||
.topbar-link.active {
|
||||
color: var(--primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.lang-switcher {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
margin-left: 8px;
|
||||
padding-left: 10px;
|
||||
border-left: 1px solid var(--gray-200);
|
||||
}
|
||||
|
||||
.lang-link {
|
||||
padding: 4px 8px;
|
||||
border-radius: 6px;
|
||||
font-family: var(--heading);
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: var(--gray-400);
|
||||
transition: color 0.15s, background 0.15s;
|
||||
}
|
||||
|
||||
.lang-link:hover {
|
||||
color: var(--gray-900);
|
||||
background: var(--gray-50);
|
||||
}
|
||||
|
||||
.lang-link.active {
|
||||
color: var(--primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.tm-container {
|
||||
max-width: 1800px;
|
||||
margin: 0 auto;
|
||||
@ -683,28 +596,17 @@ for (const b of trustBoundaries) {
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.tm-container { padding: 24px 16px; }
|
||||
.topbar-inner { padding: 0 16px; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="topbar">
|
||||
<div class="topbar-inner">
|
||||
<a href="https://openclaw.ai" class="topbar-brand">
|
||||
<img src="/openclaw-logo-text-dark.png" alt="OpenClaw" />
|
||||
</a>
|
||||
<div class="topbar-nav">
|
||||
<a href={trustHref} class="topbar-link">{t.nav.trust}</a>
|
||||
<a href={threatModelHref} class="topbar-link active">{t.nav.threatModel}</a>
|
||||
<div class="lang-switcher">
|
||||
<a href={langLinks.en.threatmodel} class="lang-link">{langLinks.en.label}</a>
|
||||
<a href={langLinks['zh-cn'].threatmodel} class="lang-link">{langLinks['zh-cn'].label}</a>
|
||||
<a href={langLinks.ko.threatmodel} class="lang-link">{langLinks.ko.label}</a>
|
||||
<a href={langLinks.ja.threatmodel} class="lang-link active">{langLinks.ja.label}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<TrustTopbar
|
||||
activePage="threatmodel"
|
||||
locale="ja"
|
||||
wide={true}
|
||||
labels={{ trust: t.nav.trust, threatModel: t.nav.threatModel }}
|
||||
langLinks={langLinks}
|
||||
/>
|
||||
|
||||
<main class="tm-container">
|
||||
<h1 class="article-title">{t.header.title}</h1>
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
---
|
||||
import TrustTopbar from '../../../components/TrustTopbar.astro';
|
||||
import t from '../../../i18n/ko/trust.json';
|
||||
|
||||
const isSubdomain = Astro.url.hostname === 'trust.openclaw.ai';
|
||||
@ -6,10 +7,10 @@ const trustHref = isSubdomain ? '/ko' : '/trust/ko';
|
||||
const threatModelHref = isSubdomain ? '/ko/threatmodel' : '/trust/ko/threatmodel';
|
||||
|
||||
const langLinks = {
|
||||
en: isSubdomain ? '/' : '/trust',
|
||||
'zh-cn': isSubdomain ? '/zh-cn' : '/trust/zh-cn',
|
||||
ko: isSubdomain ? '/ko' : '/trust/ko',
|
||||
ja: isSubdomain ? '/ja' : '/trust/ja',
|
||||
en: { label: 'EN', trust: isSubdomain ? '/' : '/trust', threatmodel: isSubdomain ? '/threatmodel' : '/trust/threatmodel' },
|
||||
'zh-cn': { label: '中文', trust: isSubdomain ? '/zh-cn' : '/trust/zh-cn', threatmodel: isSubdomain ? '/zh-cn/threatmodel' : '/trust/zh-cn/threatmodel' },
|
||||
ko: { label: '한국어', trust: trustHref, threatmodel: threatModelHref },
|
||||
ja: { label: '日本語', trust: isSubdomain ? '/ja' : '/trust/ja', threatmodel: isSubdomain ? '/ja/threatmodel' : '/trust/ja/threatmodel' },
|
||||
};
|
||||
---
|
||||
|
||||
@ -61,94 +62,6 @@ const langLinks = {
|
||||
a { color: var(--gray-500); text-decoration: none; }
|
||||
a:hover { color: var(--gray-900); }
|
||||
|
||||
/* Topbar */
|
||||
.topbar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
background: var(--bg);
|
||||
border-bottom: 1px solid var(--gray-200);
|
||||
}
|
||||
|
||||
.topbar-inner {
|
||||
max-width: 720px;
|
||||
margin: 0 auto;
|
||||
padding: 0 24px;
|
||||
height: 52px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.topbar-brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-decoration: none;
|
||||
color: var(--gray-900);
|
||||
}
|
||||
|
||||
.topbar-brand:hover { color: var(--gray-900); }
|
||||
|
||||
.topbar-brand img {
|
||||
height: 28px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.topbar-nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.topbar-link {
|
||||
padding: 6px 14px;
|
||||
border-radius: 8px;
|
||||
font-family: var(--heading);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--gray-500);
|
||||
transition: color 0.15s, background 0.15s;
|
||||
}
|
||||
|
||||
.topbar-link:hover {
|
||||
color: var(--gray-900);
|
||||
background: var(--gray-50);
|
||||
}
|
||||
|
||||
.topbar-link.active {
|
||||
color: var(--primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.lang-switcher {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
margin-left: 8px;
|
||||
padding-left: 8px;
|
||||
border-left: 1px solid var(--gray-200);
|
||||
}
|
||||
|
||||
.lang-link {
|
||||
padding: 4px 8px;
|
||||
border-radius: 6px;
|
||||
font-family: var(--heading);
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: var(--gray-400);
|
||||
transition: color 0.15s, background 0.15s;
|
||||
}
|
||||
|
||||
.lang-link:hover {
|
||||
color: var(--gray-700);
|
||||
background: var(--gray-50);
|
||||
}
|
||||
|
||||
.lang-link.active {
|
||||
color: var(--primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 720px;
|
||||
margin: 0 auto;
|
||||
@ -478,29 +391,17 @@ const langLinks = {
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.container { padding: 24px 16px; }
|
||||
.topbar-inner { padding: 0 16px; }
|
||||
.article-content p:first-child { font-size: 1.1rem; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="topbar">
|
||||
<div class="topbar-inner">
|
||||
<a href="https://openclaw.ai" class="topbar-brand">
|
||||
<img src="/openclaw-logo-text-dark.png" alt="OpenClaw" />
|
||||
</a>
|
||||
<div class="topbar-nav">
|
||||
<a href={trustHref} class="topbar-link active">{t.nav.trust}</a>
|
||||
<a href={threatModelHref} class="topbar-link">{t.nav.threatModel}</a>
|
||||
<div class="lang-switcher">
|
||||
<a href={langLinks.en} class="lang-link">EN</a>
|
||||
<a href={langLinks['zh-cn']} class="lang-link">中文</a>
|
||||
<a href={langLinks.ko} class="lang-link active">한국어</a>
|
||||
<a href={langLinks.ja} class="lang-link">日本語</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<TrustTopbar
|
||||
activePage="trust"
|
||||
locale="ko"
|
||||
labels={{ trust: t.nav.trust, threatModel: t.nav.threatModel }}
|
||||
langLinks={langLinks}
|
||||
/>
|
||||
|
||||
<main class="container">
|
||||
<article class="article">
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
---
|
||||
import TrustTopbar from '../../../components/TrustTopbar.astro';
|
||||
import yaml from 'js-yaml';
|
||||
import threatsYaml from '../../../i18n/ko/threats.yaml?raw';
|
||||
import t from '../../../i18n/ko/threatmodel.json';
|
||||
@ -8,10 +9,10 @@ const trustHref = isSubdomain ? '/ko' : '/trust/ko';
|
||||
const threatModelHref = isSubdomain ? '/ko/threatmodel' : '/trust/ko/threatmodel';
|
||||
|
||||
const langLinks = {
|
||||
en: isSubdomain ? '/threatmodel' : '/trust/threatmodel',
|
||||
'zh-cn': isSubdomain ? '/zh-cn/threatmodel' : '/trust/zh-cn/threatmodel',
|
||||
ko: isSubdomain ? '/ko/threatmodel' : '/trust/ko/threatmodel',
|
||||
ja: isSubdomain ? '/ja/threatmodel' : '/trust/ja/threatmodel',
|
||||
en: { label: 'EN', trust: isSubdomain ? '/' : '/trust', threatmodel: isSubdomain ? '/threatmodel' : '/trust/threatmodel' },
|
||||
'zh-cn': { label: '中文', trust: isSubdomain ? '/zh-cn' : '/trust/zh-cn', threatmodel: isSubdomain ? '/zh-cn/threatmodel' : '/trust/zh-cn/threatmodel' },
|
||||
ko: { label: '한국어', trust: trustHref, threatmodel: threatModelHref },
|
||||
ja: { label: '日本語', trust: isSubdomain ? '/ja' : '/trust/ja', threatmodel: isSubdomain ? '/ja/threatmodel' : '/trust/ja/threatmodel' },
|
||||
};
|
||||
|
||||
interface Threat { id: string; name: string; tactic: string; atlas: string; risk: string; category: string; description: string; attackVector: string; affected: string; mitigations: string; residualRisk: string; recommendations: string; }
|
||||
@ -110,94 +111,6 @@ for (const b of trustBoundaries) {
|
||||
a { color: var(--gray-500); text-decoration: none; }
|
||||
a:hover { color: var(--gray-900); }
|
||||
|
||||
/* Topbar */
|
||||
.topbar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
background: var(--bg);
|
||||
border-bottom: 1px solid var(--gray-200);
|
||||
}
|
||||
|
||||
.topbar-inner {
|
||||
max-width: 1800px;
|
||||
margin: 0 auto;
|
||||
padding: 0 24px;
|
||||
height: 52px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.topbar-brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-decoration: none;
|
||||
color: var(--gray-900);
|
||||
}
|
||||
|
||||
.topbar-brand:hover { color: var(--gray-900); }
|
||||
|
||||
.topbar-brand img {
|
||||
height: 28px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.topbar-nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.topbar-link {
|
||||
padding: 6px 14px;
|
||||
border-radius: 8px;
|
||||
font-family: var(--heading);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--gray-500);
|
||||
transition: color 0.15s, background 0.15s;
|
||||
}
|
||||
|
||||
.topbar-link:hover {
|
||||
color: var(--gray-900);
|
||||
background: var(--gray-50);
|
||||
}
|
||||
|
||||
.topbar-link.active {
|
||||
color: var(--primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.lang-switcher {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
margin-left: 8px;
|
||||
padding-left: 8px;
|
||||
border-left: 1px solid var(--gray-200);
|
||||
}
|
||||
|
||||
.lang-link {
|
||||
padding: 4px 8px;
|
||||
border-radius: 6px;
|
||||
font-family: var(--heading);
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: var(--gray-400);
|
||||
transition: color 0.15s, background 0.15s;
|
||||
}
|
||||
|
||||
.lang-link:hover {
|
||||
color: var(--gray-700);
|
||||
background: var(--gray-50);
|
||||
}
|
||||
|
||||
.lang-link.active {
|
||||
color: var(--primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.tm-container {
|
||||
max-width: 1800px;
|
||||
margin: 0 auto;
|
||||
@ -683,28 +596,17 @@ for (const b of trustBoundaries) {
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.tm-container { padding: 24px 16px; }
|
||||
.topbar-inner { padding: 0 16px; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="topbar">
|
||||
<div class="topbar-inner">
|
||||
<a href="https://openclaw.ai" class="topbar-brand">
|
||||
<img src="/openclaw-logo-text-dark.png" alt="OpenClaw" />
|
||||
</a>
|
||||
<div class="topbar-nav">
|
||||
<a href={trustHref} class="topbar-link">{t.nav.trust}</a>
|
||||
<a href={threatModelHref} class="topbar-link active">{t.nav.threatModel}</a>
|
||||
<div class="lang-switcher">
|
||||
<a href={langLinks.en} class="lang-link">EN</a>
|
||||
<a href={langLinks['zh-cn']} class="lang-link">中文</a>
|
||||
<a href={langLinks.ko} class="lang-link active">한국어</a>
|
||||
<a href={langLinks.ja} class="lang-link">日本語</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<TrustTopbar
|
||||
activePage="threatmodel"
|
||||
locale="ko"
|
||||
wide={true}
|
||||
labels={{ trust: t.nav.trust, threatModel: t.nav.threatModel }}
|
||||
langLinks={langLinks}
|
||||
/>
|
||||
|
||||
<main class="tm-container">
|
||||
<h1 class="article-title">{t.header.title}</h1>
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
---
|
||||
import TrustTopbar from '../../components/TrustTopbar.astro';
|
||||
import yaml from 'js-yaml';
|
||||
import threatsYaml from '../../data/threats.yaml?raw';
|
||||
|
||||
@ -109,94 +110,6 @@ for (const b of trustBoundaries) {
|
||||
a { color: var(--gray-500); text-decoration: none; }
|
||||
a:hover { color: var(--gray-900); }
|
||||
|
||||
/* Topbar */
|
||||
.topbar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
background: var(--bg);
|
||||
border-bottom: 1px solid var(--gray-200);
|
||||
}
|
||||
|
||||
.topbar-inner {
|
||||
max-width: 1800px;
|
||||
margin: 0 auto;
|
||||
padding: 0 24px;
|
||||
height: 52px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.topbar-brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-decoration: none;
|
||||
color: var(--gray-900);
|
||||
}
|
||||
|
||||
.topbar-brand:hover { color: var(--gray-900); }
|
||||
|
||||
.topbar-brand img {
|
||||
height: 28px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.topbar-nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.topbar-link {
|
||||
padding: 6px 14px;
|
||||
border-radius: 8px;
|
||||
font-family: var(--heading);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--gray-500);
|
||||
transition: color 0.15s, background 0.15s;
|
||||
}
|
||||
|
||||
.topbar-link:hover {
|
||||
color: var(--gray-900);
|
||||
background: var(--gray-50);
|
||||
}
|
||||
|
||||
.topbar-link.active {
|
||||
color: var(--primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.lang-switcher {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
margin-left: 8px;
|
||||
padding-left: 10px;
|
||||
border-left: 1px solid var(--gray-200);
|
||||
}
|
||||
|
||||
.lang-link {
|
||||
padding: 4px 8px;
|
||||
border-radius: 6px;
|
||||
font-family: var(--heading);
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: var(--gray-400);
|
||||
transition: color 0.15s, background 0.15s;
|
||||
}
|
||||
|
||||
.lang-link:hover {
|
||||
color: var(--gray-900);
|
||||
background: var(--gray-50);
|
||||
}
|
||||
|
||||
.lang-link.active {
|
||||
color: var(--primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.tm-container {
|
||||
max-width: 1800px;
|
||||
margin: 0 auto;
|
||||
@ -682,28 +595,17 @@ for (const b of trustBoundaries) {
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.tm-container { padding: 24px 16px; }
|
||||
.topbar-inner { padding: 0 16px; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="topbar">
|
||||
<div class="topbar-inner">
|
||||
<a href="https://openclaw.ai" class="topbar-brand">
|
||||
<img src="/openclaw-logo-text-dark.png" alt="OpenClaw" />
|
||||
</a>
|
||||
<div class="topbar-nav">
|
||||
<a href={trustHref} class="topbar-link">Trust</a>
|
||||
<a href={threatModelHref} class="topbar-link active">Threat Model</a>
|
||||
<div class="lang-switcher">
|
||||
<a href={langLinks.en.threatmodel} class="lang-link active">{langLinks.en.label}</a>
|
||||
<a href={langLinks['zh-cn'].threatmodel} class="lang-link">{langLinks['zh-cn'].label}</a>
|
||||
<a href={langLinks.ko.threatmodel} class="lang-link">{langLinks.ko.label}</a>
|
||||
<a href={langLinks.ja.threatmodel} class="lang-link">{langLinks.ja.label}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<TrustTopbar
|
||||
activePage="threatmodel"
|
||||
locale="en"
|
||||
wide={true}
|
||||
labels={{ trust: 'Trust', threatModel: 'Threat Model' }}
|
||||
langLinks={langLinks}
|
||||
/>
|
||||
|
||||
<main class="tm-container">
|
||||
<h1 class="article-title">Threat Model</h1>
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
---
|
||||
import TrustTopbar from '../../../components/TrustTopbar.astro';
|
||||
import t from '../../../i18n/zh-cn/trust.json';
|
||||
|
||||
const isSubdomain = Astro.url.hostname === 'trust.openclaw.ai';
|
||||
@ -61,101 +62,6 @@ const langLinks = {
|
||||
a { color: var(--gray-500); text-decoration: none; }
|
||||
a:hover { color: var(--gray-900); }
|
||||
|
||||
/* Topbar */
|
||||
.topbar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
background: var(--bg);
|
||||
border-bottom: 1px solid var(--gray-200);
|
||||
}
|
||||
|
||||
.topbar-inner {
|
||||
max-width: 720px;
|
||||
margin: 0 auto;
|
||||
padding: 0 24px;
|
||||
height: 52px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.topbar-brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-decoration: none;
|
||||
color: var(--gray-900);
|
||||
}
|
||||
|
||||
.topbar-brand:hover { color: var(--gray-900); }
|
||||
|
||||
.topbar-brand img {
|
||||
height: 28px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.topbar-nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.topbar-link {
|
||||
padding: 6px 14px;
|
||||
border-radius: 8px;
|
||||
font-family: var(--heading);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--gray-500);
|
||||
transition: color 0.15s, background 0.15s;
|
||||
}
|
||||
|
||||
.topbar-link:hover {
|
||||
color: var(--gray-900);
|
||||
background: var(--gray-50);
|
||||
}
|
||||
|
||||
.topbar-link.active {
|
||||
color: var(--primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.lang-sep {
|
||||
color: var(--gray-200);
|
||||
font-size: 13px;
|
||||
margin: 0 2px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.lang-switcher {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
margin-left: 8px;
|
||||
padding-left: 10px;
|
||||
border-left: 1px solid var(--gray-200);
|
||||
}
|
||||
|
||||
.lang-link {
|
||||
padding: 4px 8px;
|
||||
border-radius: 6px;
|
||||
font-family: var(--heading);
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: var(--gray-400);
|
||||
transition: color 0.15s, background 0.15s;
|
||||
}
|
||||
|
||||
.lang-link:hover {
|
||||
color: var(--gray-900);
|
||||
background: var(--gray-50);
|
||||
}
|
||||
|
||||
.lang-link.active {
|
||||
color: var(--primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 720px;
|
||||
margin: 0 auto;
|
||||
@ -485,29 +391,17 @@ const langLinks = {
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.container { padding: 24px 16px; }
|
||||
.topbar-inner { padding: 0 16px; }
|
||||
.article-content p:first-child { font-size: 1.1rem; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="topbar">
|
||||
<div class="topbar-inner">
|
||||
<a href="https://openclaw.ai" class="topbar-brand">
|
||||
<img src="/openclaw-logo-text-dark.png" alt="OpenClaw" />
|
||||
</a>
|
||||
<div class="topbar-nav">
|
||||
<a href={trustHref} class="topbar-link active">{t.nav.trust}</a>
|
||||
<a href={threatModelHref} class="topbar-link">{t.nav.threatModel}</a>
|
||||
<div class="lang-switcher">
|
||||
<a href={langLinks.en.trust} class="lang-link">{langLinks.en.label}</a>
|
||||
<a href={langLinks['zh-cn'].trust} class="lang-link active">{langLinks['zh-cn'].label}</a>
|
||||
<a href={langLinks.ko.trust} class="lang-link">{langLinks.ko.label}</a>
|
||||
<a href={langLinks.ja.trust} class="lang-link">{langLinks.ja.label}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<TrustTopbar
|
||||
activePage="trust"
|
||||
locale="zh-cn"
|
||||
labels={{ trust: t.nav.trust, threatModel: t.nav.threatModel }}
|
||||
langLinks={langLinks}
|
||||
/>
|
||||
|
||||
<main class="container">
|
||||
<article class="article">
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
---
|
||||
import TrustTopbar from '../../../components/TrustTopbar.astro';
|
||||
import yaml from 'js-yaml';
|
||||
import threatsYaml from '../../../i18n/zh-cn/threats.yaml?raw';
|
||||
import t from '../../../i18n/zh-cn/threatmodel.json';
|
||||
@ -110,94 +111,6 @@ for (const b of trustBoundaries) {
|
||||
a { color: var(--gray-500); text-decoration: none; }
|
||||
a:hover { color: var(--gray-900); }
|
||||
|
||||
/* Topbar */
|
||||
.topbar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
background: var(--bg);
|
||||
border-bottom: 1px solid var(--gray-200);
|
||||
}
|
||||
|
||||
.topbar-inner {
|
||||
max-width: 1800px;
|
||||
margin: 0 auto;
|
||||
padding: 0 24px;
|
||||
height: 52px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.topbar-brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-decoration: none;
|
||||
color: var(--gray-900);
|
||||
}
|
||||
|
||||
.topbar-brand:hover { color: var(--gray-900); }
|
||||
|
||||
.topbar-brand img {
|
||||
height: 28px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.topbar-nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.topbar-link {
|
||||
padding: 6px 14px;
|
||||
border-radius: 8px;
|
||||
font-family: var(--heading);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--gray-500);
|
||||
transition: color 0.15s, background 0.15s;
|
||||
}
|
||||
|
||||
.topbar-link:hover {
|
||||
color: var(--gray-900);
|
||||
background: var(--gray-50);
|
||||
}
|
||||
|
||||
.topbar-link.active {
|
||||
color: var(--primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.lang-switcher {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
margin-left: 8px;
|
||||
padding-left: 10px;
|
||||
border-left: 1px solid var(--gray-200);
|
||||
}
|
||||
|
||||
.lang-link {
|
||||
padding: 4px 8px;
|
||||
border-radius: 6px;
|
||||
font-family: var(--heading);
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: var(--gray-400);
|
||||
transition: color 0.15s, background 0.15s;
|
||||
}
|
||||
|
||||
.lang-link:hover {
|
||||
color: var(--gray-900);
|
||||
background: var(--gray-50);
|
||||
}
|
||||
|
||||
.lang-link.active {
|
||||
color: var(--primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.tm-container {
|
||||
max-width: 1800px;
|
||||
margin: 0 auto;
|
||||
@ -683,28 +596,17 @@ for (const b of trustBoundaries) {
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.tm-container { padding: 24px 16px; }
|
||||
.topbar-inner { padding: 0 16px; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="topbar">
|
||||
<div class="topbar-inner">
|
||||
<a href="https://openclaw.ai" class="topbar-brand">
|
||||
<img src="/openclaw-logo-text-dark.png" alt="OpenClaw" />
|
||||
</a>
|
||||
<div class="topbar-nav">
|
||||
<a href={trustHref} class="topbar-link">{t.nav.trust}</a>
|
||||
<a href={threatModelHref} class="topbar-link active">{t.nav.threatModel}</a>
|
||||
<div class="lang-switcher">
|
||||
<a href={langLinks.en.threatmodel} class="lang-link">{langLinks.en.label}</a>
|
||||
<a href={langLinks['zh-cn'].threatmodel} class="lang-link active">{langLinks['zh-cn'].label}</a>
|
||||
<a href={langLinks.ko.threatmodel} class="lang-link">{langLinks.ko.label}</a>
|
||||
<a href={langLinks.ja.threatmodel} class="lang-link">{langLinks.ja.label}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<TrustTopbar
|
||||
activePage="threatmodel"
|
||||
locale="zh-cn"
|
||||
wide={true}
|
||||
labels={{ trust: t.nav.trust, threatModel: t.nav.threatModel }}
|
||||
langLinks={langLinks}
|
||||
/>
|
||||
|
||||
<main class="tm-container">
|
||||
<h1 class="article-title">{t.header.title}</h1>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user