foliko 2.0.11 → 2.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +3 -1
- package/.editorconfig +56 -56
- package/.lintstagedrc +7 -7
- package/.prettierignore +29 -29
- package/.prettierrc +11 -11
- package/CLAUDE.md +1 -1
- package/Dockerfile +63 -63
- package/README.md +2 -2
- package/docs/extensions.md +3 -4
- package/docs/migration.md +3 -3
- package/docs/usage.md +3 -3
- package/install.ps1 +129 -129
- package/install.sh +121 -121
- package/package.json +1 -1
- package/plugins/core/scheduler/index.js +40 -9
- package/plugins/core/skill-manager/index.js +125 -151
- package/plugins/core/workflow/context.js +941 -941
- package/plugins/core/workflow/engine.js +66 -66
- package/plugins/core/workflow/js-runner.js +318 -318
- package/plugins/core/workflow/json-runner.js +323 -323
- package/plugins/core/workflow/stages/choice.js +74 -74
- package/plugins/core/workflow/stages/each.js +123 -123
- package/plugins/core/workflow/stages/parallel.js +69 -69
- package/plugins/executors/extension/prompt-builder.js +32 -5
- package/plugins/executors/shell/index.js +28 -0
- package/skills/find-skills/SKILL.md +133 -133
- package/skills/skill-guide/SKILL.md +99 -60
- package/src/agent/chat.js +227 -54
- package/src/utils/message-validator.js +139 -16
- package/weixin_o9cq80zgZqKPA2-s59PN43GdDy1w@im.wechat.jsonl +0 -266
package/install.ps1
CHANGED
|
@@ -1,129 +1,129 @@
|
|
|
1
|
-
# Foliko Installer
|
|
2
|
-
|
|
3
|
-
# Fix execution policy first
|
|
4
|
-
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser -Force -ErrorAction SilentlyContinue
|
|
5
|
-
|
|
6
|
-
Write-Host "Foliko Installer"
|
|
7
|
-
Write-Host "================="
|
|
8
|
-
Write-Host ""
|
|
9
|
-
|
|
10
|
-
# Refresh PATH
|
|
11
|
-
$env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
|
|
12
|
-
|
|
13
|
-
# ============ Check/Install Node.js ============
|
|
14
|
-
Write-Host "Checking Node.js..."
|
|
15
|
-
if (Get-Command node -ErrorAction SilentlyContinue) {
|
|
16
|
-
Write-Host "Node.js installed: $(node --version)" -ForegroundColor Green
|
|
17
|
-
} else {
|
|
18
|
-
Write-Host "Node.js not found, installing..." -ForegroundColor Yellow
|
|
19
|
-
|
|
20
|
-
$nodeUrl = "https://nodejs.org/dist/v20.11.0/node-v20.11.0-x64.msi"
|
|
21
|
-
$nodeInstaller = "$env:TEMP\node-installer.msi"
|
|
22
|
-
|
|
23
|
-
Write-Host "Downloading Node.js..." -ForegroundColor Cyan
|
|
24
|
-
Invoke-WebRequest -Uri $nodeUrl -OutFile $nodeInstaller -UseBasicParsing
|
|
25
|
-
|
|
26
|
-
Write-Host "Installing Node.js (may require admin)..." -ForegroundColor Cyan
|
|
27
|
-
Start-Process msiexec.exe -ArgumentList "/i", $nodeInstaller, "/quiet", "/norestart" -Wait
|
|
28
|
-
|
|
29
|
-
$env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
|
|
30
|
-
Start-Sleep -Seconds 3
|
|
31
|
-
|
|
32
|
-
if (Get-Command node -ErrorAction SilentlyContinue) {
|
|
33
|
-
Write-Host "Node.js installed: $(node --version)" -ForegroundColor Green
|
|
34
|
-
Remove-Item $nodeInstaller -Force -ErrorAction SilentlyContinue
|
|
35
|
-
} else {
|
|
36
|
-
Write-Host "Node.js installation failed" -ForegroundColor Red
|
|
37
|
-
Write-Host "Download manually: https://nodejs.org/" -ForegroundColor Yellow
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
Write-Host ""
|
|
41
|
-
|
|
42
|
-
# ============ Check/Install Python ============
|
|
43
|
-
Write-Host "Checking Python..."
|
|
44
|
-
if (Get-Command python -ErrorAction SilentlyContinue) {
|
|
45
|
-
Write-Host "Python installed: $(python --version)" -ForegroundColor Green
|
|
46
|
-
} else {
|
|
47
|
-
Write-Host "Python not found, installing..." -ForegroundColor Yellow
|
|
48
|
-
|
|
49
|
-
$pythonUrl = "https://www.python.org/ftp/python/3.12.1/python-3.12.1-amd64.exe"
|
|
50
|
-
$pythonInstaller = "$env:TEMP\python-installer.exe"
|
|
51
|
-
|
|
52
|
-
Write-Host "Downloading Python..." -ForegroundColor Cyan
|
|
53
|
-
Invoke-WebRequest -Uri $pythonUrl -OutFile $pythonInstaller -UseBasicParsing
|
|
54
|
-
|
|
55
|
-
Write-Host "Installing Python (may require admin)..." -ForegroundColor Cyan
|
|
56
|
-
Start-Process $pythonInstaller -ArgumentList "/quiet", "InstallAllUsers=1", "PrependPath=1", "Include_pip=1" -Wait
|
|
57
|
-
|
|
58
|
-
$env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
|
|
59
|
-
Start-Sleep -Seconds 3
|
|
60
|
-
|
|
61
|
-
if (Get-Command python -ErrorAction SilentlyContinue) {
|
|
62
|
-
Write-Host "Python installed: $(python --version)" -ForegroundColor Green
|
|
63
|
-
Remove-Item $pythonInstaller -Force -ErrorAction SilentlyContinue
|
|
64
|
-
} else {
|
|
65
|
-
Write-Host "Python installation failed" -ForegroundColor Red
|
|
66
|
-
Write-Host "Download manually: https://www.python.org/downloads/" -ForegroundColor Yellow
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
Write-Host ""
|
|
70
|
-
|
|
71
|
-
# ============ Check/Install uv ============
|
|
72
|
-
Write-Host "Checking uv..."
|
|
73
|
-
if (Get-Command uv -ErrorAction SilentlyContinue) {
|
|
74
|
-
Write-Host "uv installed: $(uv --version)" -ForegroundColor Green
|
|
75
|
-
} else {
|
|
76
|
-
Write-Host "uv not found, installing..." -ForegroundColor Yellow
|
|
77
|
-
|
|
78
|
-
try {
|
|
79
|
-
Write-Host "Downloading uv..." -ForegroundColor Cyan
|
|
80
|
-
Invoke-WebRequest -Uri "https://astral.sh/uv/install.ps1" -OutFile "$env:TEMP\install-uv.ps1" -UseBasicParsing
|
|
81
|
-
powershell -ExecutionPolicy Bypass -File "$env:TEMP\install-uv.ps1" -Version "0.4.0" -PowerShell -Admin
|
|
82
|
-
Remove-Item "$env:TEMP\install-uv.ps1" -Force -ErrorAction SilentlyContinue
|
|
83
|
-
|
|
84
|
-
$env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
|
|
85
|
-
|
|
86
|
-
if (Get-Command uv -ErrorAction SilentlyContinue) {
|
|
87
|
-
Write-Host "uv installed: $(uv --version)" -ForegroundColor Green
|
|
88
|
-
}
|
|
89
|
-
} catch {
|
|
90
|
-
Write-Host "uv installation failed (optional, can be skipped)" -ForegroundColor Yellow
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
Write-Host ""
|
|
94
|
-
|
|
95
|
-
# ============ Clean npm cache and install Foliko ============
|
|
96
|
-
Write-Host "Cleaning npm cache..." -ForegroundColor Cyan
|
|
97
|
-
npm cache clean --force 2>$null | Out-Null
|
|
98
|
-
|
|
99
|
-
Write-Host "Installing Foliko..." -ForegroundColor Cyan
|
|
100
|
-
npm install -g foliko --ignore-scripts 2>&1 | Out-Null
|
|
101
|
-
|
|
102
|
-
# Get npm global bin path
|
|
103
|
-
$npmPrefix = npm config get prefix 2>$null
|
|
104
|
-
$npmBinPath = $npmPrefix -replace "\\$", ""
|
|
105
|
-
|
|
106
|
-
# Add to PATH permanently for current user
|
|
107
|
-
$userPath = [Environment]::GetEnvironmentVariable("Path", "User")
|
|
108
|
-
if ($userPath -notlike "*$npmBinPath*") {
|
|
109
|
-
[Environment]::SetEnvironmentVariable("Path", "$userPath;$npmBinPath", "User")
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
# Also add to current session PATH
|
|
113
|
-
$env:Path = "$npmBinPath;$env:Path"
|
|
114
|
-
Start-Sleep -Seconds 2
|
|
115
|
-
|
|
116
|
-
# Check if installation succeeded
|
|
117
|
-
if (Test-Path "$npmBinPath\foliko.cmd") {
|
|
118
|
-
Write-Host ""
|
|
119
|
-
Write-Host "Installation complete!" -ForegroundColor Green
|
|
120
|
-
Write-Host ""
|
|
121
|
-
Write-Host "Please restart your terminal or run:" -ForegroundColor Cyan
|
|
122
|
-
Write-Host ' $env:Path = "C:\Users\Administrator\AppData\Roaming\npm;' + '$env:Path"' -ForegroundColor White
|
|
123
|
-
Write-Host ""
|
|
124
|
-
Write-Host "Then run: foliko chat" -ForegroundColor Yellow
|
|
125
|
-
} else {
|
|
126
|
-
Write-Host ""
|
|
127
|
-
Write-Host "Installation may have failed." -ForegroundColor Yellow
|
|
128
|
-
Write-Host "Run: npm install -g foliko"
|
|
129
|
-
}
|
|
1
|
+
# Foliko Installer
|
|
2
|
+
|
|
3
|
+
# Fix execution policy first
|
|
4
|
+
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser -Force -ErrorAction SilentlyContinue
|
|
5
|
+
|
|
6
|
+
Write-Host "Foliko Installer"
|
|
7
|
+
Write-Host "================="
|
|
8
|
+
Write-Host ""
|
|
9
|
+
|
|
10
|
+
# Refresh PATH
|
|
11
|
+
$env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
|
|
12
|
+
|
|
13
|
+
# ============ Check/Install Node.js ============
|
|
14
|
+
Write-Host "Checking Node.js..."
|
|
15
|
+
if (Get-Command node -ErrorAction SilentlyContinue) {
|
|
16
|
+
Write-Host "Node.js installed: $(node --version)" -ForegroundColor Green
|
|
17
|
+
} else {
|
|
18
|
+
Write-Host "Node.js not found, installing..." -ForegroundColor Yellow
|
|
19
|
+
|
|
20
|
+
$nodeUrl = "https://nodejs.org/dist/v20.11.0/node-v20.11.0-x64.msi"
|
|
21
|
+
$nodeInstaller = "$env:TEMP\node-installer.msi"
|
|
22
|
+
|
|
23
|
+
Write-Host "Downloading Node.js..." -ForegroundColor Cyan
|
|
24
|
+
Invoke-WebRequest -Uri $nodeUrl -OutFile $nodeInstaller -UseBasicParsing
|
|
25
|
+
|
|
26
|
+
Write-Host "Installing Node.js (may require admin)..." -ForegroundColor Cyan
|
|
27
|
+
Start-Process msiexec.exe -ArgumentList "/i", $nodeInstaller, "/quiet", "/norestart" -Wait
|
|
28
|
+
|
|
29
|
+
$env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
|
|
30
|
+
Start-Sleep -Seconds 3
|
|
31
|
+
|
|
32
|
+
if (Get-Command node -ErrorAction SilentlyContinue) {
|
|
33
|
+
Write-Host "Node.js installed: $(node --version)" -ForegroundColor Green
|
|
34
|
+
Remove-Item $nodeInstaller -Force -ErrorAction SilentlyContinue
|
|
35
|
+
} else {
|
|
36
|
+
Write-Host "Node.js installation failed" -ForegroundColor Red
|
|
37
|
+
Write-Host "Download manually: https://nodejs.org/" -ForegroundColor Yellow
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
Write-Host ""
|
|
41
|
+
|
|
42
|
+
# ============ Check/Install Python ============
|
|
43
|
+
Write-Host "Checking Python..."
|
|
44
|
+
if (Get-Command python -ErrorAction SilentlyContinue) {
|
|
45
|
+
Write-Host "Python installed: $(python --version)" -ForegroundColor Green
|
|
46
|
+
} else {
|
|
47
|
+
Write-Host "Python not found, installing..." -ForegroundColor Yellow
|
|
48
|
+
|
|
49
|
+
$pythonUrl = "https://www.python.org/ftp/python/3.12.1/python-3.12.1-amd64.exe"
|
|
50
|
+
$pythonInstaller = "$env:TEMP\python-installer.exe"
|
|
51
|
+
|
|
52
|
+
Write-Host "Downloading Python..." -ForegroundColor Cyan
|
|
53
|
+
Invoke-WebRequest -Uri $pythonUrl -OutFile $pythonInstaller -UseBasicParsing
|
|
54
|
+
|
|
55
|
+
Write-Host "Installing Python (may require admin)..." -ForegroundColor Cyan
|
|
56
|
+
Start-Process $pythonInstaller -ArgumentList "/quiet", "InstallAllUsers=1", "PrependPath=1", "Include_pip=1" -Wait
|
|
57
|
+
|
|
58
|
+
$env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
|
|
59
|
+
Start-Sleep -Seconds 3
|
|
60
|
+
|
|
61
|
+
if (Get-Command python -ErrorAction SilentlyContinue) {
|
|
62
|
+
Write-Host "Python installed: $(python --version)" -ForegroundColor Green
|
|
63
|
+
Remove-Item $pythonInstaller -Force -ErrorAction SilentlyContinue
|
|
64
|
+
} else {
|
|
65
|
+
Write-Host "Python installation failed" -ForegroundColor Red
|
|
66
|
+
Write-Host "Download manually: https://www.python.org/downloads/" -ForegroundColor Yellow
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
Write-Host ""
|
|
70
|
+
|
|
71
|
+
# ============ Check/Install uv ============
|
|
72
|
+
Write-Host "Checking uv..."
|
|
73
|
+
if (Get-Command uv -ErrorAction SilentlyContinue) {
|
|
74
|
+
Write-Host "uv installed: $(uv --version)" -ForegroundColor Green
|
|
75
|
+
} else {
|
|
76
|
+
Write-Host "uv not found, installing..." -ForegroundColor Yellow
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
Write-Host "Downloading uv..." -ForegroundColor Cyan
|
|
80
|
+
Invoke-WebRequest -Uri "https://astral.sh/uv/install.ps1" -OutFile "$env:TEMP\install-uv.ps1" -UseBasicParsing
|
|
81
|
+
powershell -ExecutionPolicy Bypass -File "$env:TEMP\install-uv.ps1" -Version "0.4.0" -PowerShell -Admin
|
|
82
|
+
Remove-Item "$env:TEMP\install-uv.ps1" -Force -ErrorAction SilentlyContinue
|
|
83
|
+
|
|
84
|
+
$env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
|
|
85
|
+
|
|
86
|
+
if (Get-Command uv -ErrorAction SilentlyContinue) {
|
|
87
|
+
Write-Host "uv installed: $(uv --version)" -ForegroundColor Green
|
|
88
|
+
}
|
|
89
|
+
} catch {
|
|
90
|
+
Write-Host "uv installation failed (optional, can be skipped)" -ForegroundColor Yellow
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
Write-Host ""
|
|
94
|
+
|
|
95
|
+
# ============ Clean npm cache and install Foliko ============
|
|
96
|
+
Write-Host "Cleaning npm cache..." -ForegroundColor Cyan
|
|
97
|
+
npm cache clean --force 2>$null | Out-Null
|
|
98
|
+
|
|
99
|
+
Write-Host "Installing Foliko..." -ForegroundColor Cyan
|
|
100
|
+
npm install -g foliko --ignore-scripts 2>&1 | Out-Null
|
|
101
|
+
|
|
102
|
+
# Get npm global bin path
|
|
103
|
+
$npmPrefix = npm config get prefix 2>$null
|
|
104
|
+
$npmBinPath = $npmPrefix -replace "\\$", ""
|
|
105
|
+
|
|
106
|
+
# Add to PATH permanently for current user
|
|
107
|
+
$userPath = [Environment]::GetEnvironmentVariable("Path", "User")
|
|
108
|
+
if ($userPath -notlike "*$npmBinPath*") {
|
|
109
|
+
[Environment]::SetEnvironmentVariable("Path", "$userPath;$npmBinPath", "User")
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
# Also add to current session PATH
|
|
113
|
+
$env:Path = "$npmBinPath;$env:Path"
|
|
114
|
+
Start-Sleep -Seconds 2
|
|
115
|
+
|
|
116
|
+
# Check if installation succeeded
|
|
117
|
+
if (Test-Path "$npmBinPath\foliko.cmd") {
|
|
118
|
+
Write-Host ""
|
|
119
|
+
Write-Host "Installation complete!" -ForegroundColor Green
|
|
120
|
+
Write-Host ""
|
|
121
|
+
Write-Host "Please restart your terminal or run:" -ForegroundColor Cyan
|
|
122
|
+
Write-Host ' $env:Path = "C:\Users\Administrator\AppData\Roaming\npm;' + '$env:Path"' -ForegroundColor White
|
|
123
|
+
Write-Host ""
|
|
124
|
+
Write-Host "Then run: foliko chat" -ForegroundColor Yellow
|
|
125
|
+
} else {
|
|
126
|
+
Write-Host ""
|
|
127
|
+
Write-Host "Installation may have failed." -ForegroundColor Yellow
|
|
128
|
+
Write-Host "Run: npm install -g foliko"
|
|
129
|
+
}
|
package/install.sh
CHANGED
|
@@ -1,121 +1,121 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Foliko Installer
|
|
3
|
-
|
|
4
|
-
set -e
|
|
5
|
-
|
|
6
|
-
echo -e "\033[36mFoliko Installer\033[0m"
|
|
7
|
-
echo -e "\033[36m=================\033[0m"
|
|
8
|
-
echo ""
|
|
9
|
-
|
|
10
|
-
# Detect OS
|
|
11
|
-
detect_os() {
|
|
12
|
-
if [[ "$OSTYPE" == "darwin"* ]]; then
|
|
13
|
-
echo "macOS"
|
|
14
|
-
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
|
15
|
-
echo "Linux"
|
|
16
|
-
else
|
|
17
|
-
echo "unknown"
|
|
18
|
-
fi
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
OS=$(detect_os)
|
|
22
|
-
|
|
23
|
-
# ============ Check/Install Node.js ============
|
|
24
|
-
echo -e "\033[36mChecking Node.js...\033[0m"
|
|
25
|
-
if command -v node &> /dev/null; then
|
|
26
|
-
echo -e "\033[32mNode.js installed: $(node --version)\033[0m"
|
|
27
|
-
else
|
|
28
|
-
echo -e "\033[33mNode.js not found, installing...\033[0m"
|
|
29
|
-
|
|
30
|
-
if [[ "$OS" == "macOS" ]]; then
|
|
31
|
-
if command -v brew &> /dev/null; then
|
|
32
|
-
brew install node
|
|
33
|
-
else
|
|
34
|
-
echo -e "\033[31mHomebrew not found. Install it first:\033[0m"
|
|
35
|
-
echo "/bin/bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\""
|
|
36
|
-
exit 1
|
|
37
|
-
fi
|
|
38
|
-
elif [[ "$OS" == "Linux" ]]; then
|
|
39
|
-
if command -v apt-get &> /dev/null; then
|
|
40
|
-
sudo apt-get update && sudo apt-get install -y nodejs npm
|
|
41
|
-
elif command -v yum &> /dev/null; then
|
|
42
|
-
sudo yum install -y nodejs npm
|
|
43
|
-
elif command -v pacman &> /dev/null; then
|
|
44
|
-
sudo pacman -S nodejs npm
|
|
45
|
-
else
|
|
46
|
-
echo -e "\033[31mNo package manager found. Install Node.js manually: https://nodejs.org/\033[0m"
|
|
47
|
-
exit 1
|
|
48
|
-
fi
|
|
49
|
-
fi
|
|
50
|
-
|
|
51
|
-
if command -v node &> /dev/null; then
|
|
52
|
-
echo -e "\033[32mNode.js installed: $(node --version)\033[0m"
|
|
53
|
-
else
|
|
54
|
-
echo -e "\033[31mNode.js installation failed\033[0m"
|
|
55
|
-
exit 1
|
|
56
|
-
fi
|
|
57
|
-
fi
|
|
58
|
-
echo ""
|
|
59
|
-
|
|
60
|
-
# ============ Check/Install Python ============
|
|
61
|
-
echo -e "\033[36mChecking Python...\033[0m"
|
|
62
|
-
if command -v python3 &> /dev/null; then
|
|
63
|
-
echo -e "\033[32mPython installed: $(python3 --version)\033[0m"
|
|
64
|
-
else
|
|
65
|
-
echo -e "\033[33mPython not found, installing...\033[0m"
|
|
66
|
-
|
|
67
|
-
if [[ "$OS" == "macOS" ]]; then
|
|
68
|
-
if command -v brew &> /dev/null; then
|
|
69
|
-
brew install python3
|
|
70
|
-
fi
|
|
71
|
-
elif [[ "$OS" == "Linux" ]]; then
|
|
72
|
-
if command -v apt-get &> /dev/null; then
|
|
73
|
-
sudo apt-get update && sudo apt-get install -y python3 python3-venv python3-pip
|
|
74
|
-
elif command -v yum &> /dev/null; then
|
|
75
|
-
sudo yum install -y python3
|
|
76
|
-
elif command -v pacman &> /dev/null; then
|
|
77
|
-
sudo pacman -S python python-pip
|
|
78
|
-
fi
|
|
79
|
-
fi
|
|
80
|
-
|
|
81
|
-
if command -v python3 &> /dev/null; then
|
|
82
|
-
echo -e "\033[32mPython installed: $(python3 --version)\033[0m"
|
|
83
|
-
else
|
|
84
|
-
echo -e "\033[33mPython installation failed (optional)\033[0m"
|
|
85
|
-
fi
|
|
86
|
-
fi
|
|
87
|
-
echo ""
|
|
88
|
-
|
|
89
|
-
# ============ Check/Install uv ============
|
|
90
|
-
echo -e "\033[36mChecking uv...\033[0m"
|
|
91
|
-
if command -v uv &> /dev/null; then
|
|
92
|
-
echo -e "\033[32muv installed: $(uv --version)\033[0m"
|
|
93
|
-
else
|
|
94
|
-
echo -e "\033[33muv not found, installing...\033[0m"
|
|
95
|
-
|
|
96
|
-
if [[ "$OS" == "macOS" ]] || [[ "$OS" == "Linux" ]]; then
|
|
97
|
-
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
98
|
-
source $HOME/.local/bin/env 2>/dev/null || true
|
|
99
|
-
|
|
100
|
-
if command -v uv &> /dev/null; then
|
|
101
|
-
echo -e "\033[32muv installed: $(uv --version)\033[0m"
|
|
102
|
-
else
|
|
103
|
-
echo -e "\033[33muv installation failed (optional)\033[0m"
|
|
104
|
-
fi
|
|
105
|
-
fi
|
|
106
|
-
fi
|
|
107
|
-
echo ""
|
|
108
|
-
|
|
109
|
-
# ============ Install Foliko ============
|
|
110
|
-
echo -e "\033[36mInstalling Foliko...\033[0m"
|
|
111
|
-
npm install -g foliko
|
|
112
|
-
|
|
113
|
-
if command -v foliko &> /dev/null; then
|
|
114
|
-
echo ""
|
|
115
|
-
echo -e "\033[32mInstallation complete!\033[0m"
|
|
116
|
-
echo "Run: foliko chat"
|
|
117
|
-
else
|
|
118
|
-
echo ""
|
|
119
|
-
echo -e "\033[31mInstallation failed. Try manually: npm install -g foliko\033[0m"
|
|
120
|
-
exit 1
|
|
121
|
-
fi
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Foliko Installer
|
|
3
|
+
|
|
4
|
+
set -e
|
|
5
|
+
|
|
6
|
+
echo -e "\033[36mFoliko Installer\033[0m"
|
|
7
|
+
echo -e "\033[36m=================\033[0m"
|
|
8
|
+
echo ""
|
|
9
|
+
|
|
10
|
+
# Detect OS
|
|
11
|
+
detect_os() {
|
|
12
|
+
if [[ "$OSTYPE" == "darwin"* ]]; then
|
|
13
|
+
echo "macOS"
|
|
14
|
+
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
|
15
|
+
echo "Linux"
|
|
16
|
+
else
|
|
17
|
+
echo "unknown"
|
|
18
|
+
fi
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
OS=$(detect_os)
|
|
22
|
+
|
|
23
|
+
# ============ Check/Install Node.js ============
|
|
24
|
+
echo -e "\033[36mChecking Node.js...\033[0m"
|
|
25
|
+
if command -v node &> /dev/null; then
|
|
26
|
+
echo -e "\033[32mNode.js installed: $(node --version)\033[0m"
|
|
27
|
+
else
|
|
28
|
+
echo -e "\033[33mNode.js not found, installing...\033[0m"
|
|
29
|
+
|
|
30
|
+
if [[ "$OS" == "macOS" ]]; then
|
|
31
|
+
if command -v brew &> /dev/null; then
|
|
32
|
+
brew install node
|
|
33
|
+
else
|
|
34
|
+
echo -e "\033[31mHomebrew not found. Install it first:\033[0m"
|
|
35
|
+
echo "/bin/bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\""
|
|
36
|
+
exit 1
|
|
37
|
+
fi
|
|
38
|
+
elif [[ "$OS" == "Linux" ]]; then
|
|
39
|
+
if command -v apt-get &> /dev/null; then
|
|
40
|
+
sudo apt-get update && sudo apt-get install -y nodejs npm
|
|
41
|
+
elif command -v yum &> /dev/null; then
|
|
42
|
+
sudo yum install -y nodejs npm
|
|
43
|
+
elif command -v pacman &> /dev/null; then
|
|
44
|
+
sudo pacman -S nodejs npm
|
|
45
|
+
else
|
|
46
|
+
echo -e "\033[31mNo package manager found. Install Node.js manually: https://nodejs.org/\033[0m"
|
|
47
|
+
exit 1
|
|
48
|
+
fi
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
if command -v node &> /dev/null; then
|
|
52
|
+
echo -e "\033[32mNode.js installed: $(node --version)\033[0m"
|
|
53
|
+
else
|
|
54
|
+
echo -e "\033[31mNode.js installation failed\033[0m"
|
|
55
|
+
exit 1
|
|
56
|
+
fi
|
|
57
|
+
fi
|
|
58
|
+
echo ""
|
|
59
|
+
|
|
60
|
+
# ============ Check/Install Python ============
|
|
61
|
+
echo -e "\033[36mChecking Python...\033[0m"
|
|
62
|
+
if command -v python3 &> /dev/null; then
|
|
63
|
+
echo -e "\033[32mPython installed: $(python3 --version)\033[0m"
|
|
64
|
+
else
|
|
65
|
+
echo -e "\033[33mPython not found, installing...\033[0m"
|
|
66
|
+
|
|
67
|
+
if [[ "$OS" == "macOS" ]]; then
|
|
68
|
+
if command -v brew &> /dev/null; then
|
|
69
|
+
brew install python3
|
|
70
|
+
fi
|
|
71
|
+
elif [[ "$OS" == "Linux" ]]; then
|
|
72
|
+
if command -v apt-get &> /dev/null; then
|
|
73
|
+
sudo apt-get update && sudo apt-get install -y python3 python3-venv python3-pip
|
|
74
|
+
elif command -v yum &> /dev/null; then
|
|
75
|
+
sudo yum install -y python3
|
|
76
|
+
elif command -v pacman &> /dev/null; then
|
|
77
|
+
sudo pacman -S python python-pip
|
|
78
|
+
fi
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
if command -v python3 &> /dev/null; then
|
|
82
|
+
echo -e "\033[32mPython installed: $(python3 --version)\033[0m"
|
|
83
|
+
else
|
|
84
|
+
echo -e "\033[33mPython installation failed (optional)\033[0m"
|
|
85
|
+
fi
|
|
86
|
+
fi
|
|
87
|
+
echo ""
|
|
88
|
+
|
|
89
|
+
# ============ Check/Install uv ============
|
|
90
|
+
echo -e "\033[36mChecking uv...\033[0m"
|
|
91
|
+
if command -v uv &> /dev/null; then
|
|
92
|
+
echo -e "\033[32muv installed: $(uv --version)\033[0m"
|
|
93
|
+
else
|
|
94
|
+
echo -e "\033[33muv not found, installing...\033[0m"
|
|
95
|
+
|
|
96
|
+
if [[ "$OS" == "macOS" ]] || [[ "$OS" == "Linux" ]]; then
|
|
97
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
98
|
+
source $HOME/.local/bin/env 2>/dev/null || true
|
|
99
|
+
|
|
100
|
+
if command -v uv &> /dev/null; then
|
|
101
|
+
echo -e "\033[32muv installed: $(uv --version)\033[0m"
|
|
102
|
+
else
|
|
103
|
+
echo -e "\033[33muv installation failed (optional)\033[0m"
|
|
104
|
+
fi
|
|
105
|
+
fi
|
|
106
|
+
fi
|
|
107
|
+
echo ""
|
|
108
|
+
|
|
109
|
+
# ============ Install Foliko ============
|
|
110
|
+
echo -e "\033[36mInstalling Foliko...\033[0m"
|
|
111
|
+
npm install -g foliko
|
|
112
|
+
|
|
113
|
+
if command -v foliko &> /dev/null; then
|
|
114
|
+
echo ""
|
|
115
|
+
echo -e "\033[32mInstallation complete!\033[0m"
|
|
116
|
+
echo "Run: foliko chat"
|
|
117
|
+
else
|
|
118
|
+
echo ""
|
|
119
|
+
echo -e "\033[31mInstallation failed. Try manually: npm install -g foliko\033[0m"
|
|
120
|
+
exit 1
|
|
121
|
+
fi
|
package/package.json
CHANGED
|
@@ -340,6 +340,7 @@ class SchedulerPlugin extends Plugin {
|
|
|
340
340
|
}),
|
|
341
341
|
});
|
|
342
342
|
|
|
343
|
+
log.info(` [start] Scheduler 插件启动,当前时间: ${new Date().toISOString()} timezone=${this.config.timezone}`);
|
|
343
344
|
this._loadPersistedTasks();
|
|
344
345
|
return this;
|
|
345
346
|
}
|
|
@@ -432,7 +433,11 @@ class SchedulerPlugin extends Plugin {
|
|
|
432
433
|
// once:startAt 限定未来触发
|
|
433
434
|
task.scheduleHandle = new Cron(runAt, this._cronOptions(task), () => this._onFire(task));
|
|
434
435
|
}
|
|
436
|
+
// 打印下次执行时间便于调试
|
|
437
|
+
const nextRun = task.scheduleHandle.nextRun();
|
|
438
|
+
log.info(` [_createTask] 创建任务成功: ${task.name} cron="${cron || ''}" 下次执行: ${nextRun ? nextRun.toISOString() : 'once-立即?'} timezone=${task.timezone}`);
|
|
435
439
|
} catch (err) {
|
|
440
|
+
log.error(` [_createTask] 创建调度失败: ${err.message}`);
|
|
436
441
|
return { success: false, error: `调度创建失败: ${err.message}` };
|
|
437
442
|
}
|
|
438
443
|
|
|
@@ -465,30 +470,39 @@ class SchedulerPlugin extends Plugin {
|
|
|
465
470
|
}
|
|
466
471
|
|
|
467
472
|
_cronOptions(task) {
|
|
468
|
-
|
|
473
|
+
const opts = {
|
|
469
474
|
timezone: this.config.timezone,
|
|
470
475
|
protect: true, // 防重叠:上一次还没跑完,跳过本次
|
|
471
476
|
catch: true, // catch-up:进程恢复后补触发错过的触发
|
|
472
477
|
name: `scheduler:${task.id}`,
|
|
473
478
|
unref: true, // 不阻止进程退出
|
|
474
479
|
};
|
|
480
|
+
log.debug(` [_cronOptions] task=${task.name} cron=${task.cronExpression || 'once'} timezone=${opts.timezone}`);
|
|
481
|
+
return opts;
|
|
475
482
|
}
|
|
476
483
|
|
|
477
484
|
// ============================================================
|
|
478
485
|
// 触发回调
|
|
479
486
|
// ============================================================
|
|
480
487
|
async _onFire(task) {
|
|
481
|
-
|
|
488
|
+
log.info(` [_onFire] >>> 任务触发: ${task.name} (id=${task.id}) cron=${task.cronExpression || 'once'} timezone=${task.timezone}`);
|
|
489
|
+
|
|
490
|
+
if (!task.enabled) {
|
|
491
|
+
log.warn(` [_onFire] 任务 ${task.name} 已禁用,跳过`);
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
482
494
|
if (task.running) {
|
|
483
495
|
// 理论上 protect:true 已经处理过;这里再保险一次
|
|
484
|
-
log.warn(` 任务 ${task.name} 仍在执行中,跳过本次触发`);
|
|
496
|
+
log.warn(` [_onFire] 任务 ${task.name} 仍在执行中,跳过本次触发`);
|
|
485
497
|
return;
|
|
486
498
|
}
|
|
487
499
|
task.running = true;
|
|
488
500
|
task.lastRun = new Date();
|
|
489
501
|
task.runCount++;
|
|
502
|
+
log.info(` [_onFire] 开始执行任务: ${task.name} (第${task.runCount}次)`);
|
|
490
503
|
try {
|
|
491
504
|
await this._executeTask(task);
|
|
505
|
+
log.info(` [_onFire] <<< 任务执行完成: ${task.name}`);
|
|
492
506
|
} finally {
|
|
493
507
|
task.running = false;
|
|
494
508
|
}
|
|
@@ -890,12 +904,20 @@ class SchedulerPlugin extends Plugin {
|
|
|
890
904
|
// ============================================================
|
|
891
905
|
_loadPersistedTasks() {
|
|
892
906
|
const savedTasks = this._taskStore.loadTasks();
|
|
893
|
-
|
|
907
|
+
log.info(` [_loadPersistedTasks] 开始加载任务,当前时间: ${new Date().toISOString()}`);
|
|
908
|
+
if (!savedTasks || savedTasks.length === 0) {
|
|
909
|
+
log.info(` [_loadPersistedTasks] 没有已保存的任务`);
|
|
910
|
+
return;
|
|
911
|
+
}
|
|
912
|
+
log.info(` [_loadPersistedTasks] 从磁盘加载了 ${savedTasks.length} 个任务`);
|
|
894
913
|
const now = Date.now();
|
|
895
914
|
|
|
896
915
|
for (const saved of savedTasks) {
|
|
897
916
|
// 已完成的 once 任务不再恢复
|
|
898
|
-
if (saved.type === 'once' && (saved.runCount || 0) > 0)
|
|
917
|
+
if (saved.type === 'once' && (saved.runCount || 0) > 0) {
|
|
918
|
+
log.debug(` [_loadPersistedTasks] 跳过已完成的 once 任务: ${saved.name}`);
|
|
919
|
+
continue;
|
|
920
|
+
}
|
|
899
921
|
|
|
900
922
|
const task = {
|
|
901
923
|
...saved,
|
|
@@ -910,19 +932,25 @@ class SchedulerPlugin extends Plugin {
|
|
|
910
932
|
try {
|
|
911
933
|
if (task.type === 'cron' && task.enabled && task.cronExpression) {
|
|
912
934
|
task.scheduleHandle = new Cron(task.cronExpression, this._cronOptions(task), () => this._onFire(task));
|
|
935
|
+
const nextRun = task.scheduleHandle.nextRun();
|
|
936
|
+
log.info(` [_loadPersistedTasks] 恢复 cron 任务: ${task.name} cron="${task.cronExpression}" 下次执行: ${nextRun ? nextRun.toISOString() : 'N/A'}`);
|
|
913
937
|
this._tasks.set(task.id, task);
|
|
914
938
|
} else if (task.type === 'once' && task.enabled) {
|
|
915
|
-
if (!task.runAt)
|
|
939
|
+
if (!task.runAt) {
|
|
940
|
+
log.warn(` [_loadPersistedTasks] 跳过没有 runAt 的 once 任务: ${task.name}`);
|
|
941
|
+
continue;
|
|
942
|
+
}
|
|
916
943
|
const fireAt = task.runAt.getTime();
|
|
917
944
|
if (fireAt <= now) {
|
|
918
945
|
// 已过期:超过 grace period 就丢弃
|
|
919
946
|
if (now - fireAt > ONCE_EXPIRY_GRACE_MS) {
|
|
920
|
-
log.warn(` 丢弃过期任务 ${task.name}(已过期 ${Math.round((now - fireAt) / 1000)}s,超出 24h 容忍期)`);
|
|
947
|
+
log.warn(` [_loadPersistedTasks] 丢弃过期任务 ${task.name}(已过期 ${Math.round((now - fireAt) / 1000)}s,超出 24h 容忍期)`);
|
|
921
948
|
continue;
|
|
922
949
|
}
|
|
923
950
|
// B4/B5 修复:catchup 也用 croner 调度过去时间,croner 会立即触发;
|
|
924
951
|
// stop() 能真正取消,update 走 stop()+new Cron() 不会双触发
|
|
925
952
|
// protect:false 是因为"补触发"不应被 startup 期间的并发阻塞
|
|
953
|
+
log.info(` [_loadPersistedTasks] 恢复过期的 once 任务(立即触发): ${task.name} 原定执行: ${task.runAt.toISOString()}`);
|
|
926
954
|
task.scheduleHandle = new Cron(task.runAt, {
|
|
927
955
|
...this._cronOptions(task),
|
|
928
956
|
protect: false,
|
|
@@ -930,15 +958,18 @@ class SchedulerPlugin extends Plugin {
|
|
|
930
958
|
this._tasks.set(task.id, task);
|
|
931
959
|
} else {
|
|
932
960
|
// 未来触发
|
|
961
|
+
log.info(` [_loadPersistedTasks] 恢复 future once 任务: ${task.name} 执行时间: ${task.runAt.toISOString()}`);
|
|
933
962
|
task.scheduleHandle = new Cron(task.runAt, this._cronOptions(task), () => this._onFire(task));
|
|
934
963
|
this._tasks.set(task.id, task);
|
|
935
964
|
}
|
|
965
|
+
} else {
|
|
966
|
+
log.debug(` [_loadPersistedTasks] 跳过任务: ${task.name} type=${task.type} enabled=${task.enabled}`);
|
|
936
967
|
}
|
|
937
968
|
} catch (err) {
|
|
938
|
-
log.error(` 恢复任务失败: ${task.name}`, err.message);
|
|
969
|
+
log.error(` [_loadPersistedTasks] 恢复任务失败: ${task.name}`, err.message);
|
|
939
970
|
}
|
|
940
971
|
}
|
|
941
|
-
log.info(`
|
|
972
|
+
log.info(` [_loadPersistedTasks] 完成,共恢复 ${this._tasks.size} 个定时任务`);
|
|
942
973
|
}
|
|
943
974
|
|
|
944
975
|
// ============================================================
|