@covibes/zeroshot 4.1.2 → 4.1.4
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/CHANGELOG.md +15 -0
- package/package.json +4 -2
- package/scripts/fix-node-pty-permissions.js +75 -0
- package/scripts/record-demo.sh +279 -0
- package/scripts/test-install.sh +40 -0
- package/src/preflight.js +37 -1
- package/task-lib/commands/list.js +2 -2
- package/task-lib/commands/status.js +1 -1
- package/task-lib/tui.js +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
## [4.1.4](https://github.com/covibes/zeroshot/compare/v4.1.3...v4.1.4) (2026-01-06)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **ci:** enforce CI testing for all releases ([3fad703](https://github.com/covibes/zeroshot/commit/3fad703fb6eb6b7ac8f98400466bac92329c8561))
|
|
7
|
+
|
|
8
|
+
## [4.1.3](https://github.com/covibes/zeroshot/compare/v4.1.2...v4.1.3) (2026-01-06)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **package:** include scripts/ in npm package files ([4bd5991](https://github.com/covibes/zeroshot/commit/4bd599163df5c7b7f9309f07c4af4b1ec5e7bf38))
|
|
14
|
+
* **preflight:** support macOS Keychain auth detection ([#35](https://github.com/covibes/zeroshot/issues/35)) ([a6f0880](https://github.com/covibes/zeroshot/commit/a6f08807eb2b44b241800a2240a76bf6dbedceca))
|
|
15
|
+
|
|
1
16
|
## [4.1.2](https://github.com/covibes/zeroshot/compare/v4.1.1...v4.1.2) (2026-01-05)
|
|
2
17
|
|
|
3
18
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@covibes/zeroshot",
|
|
3
|
-
"version": "4.1.
|
|
3
|
+
"version": "4.1.4",
|
|
4
4
|
"description": "Multi-agent orchestration engine for Claude - cluster coordinator and CLI",
|
|
5
5
|
"main": "src/orchestrator.js",
|
|
6
6
|
"bin": {
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"test": "mocha 'tests/**/*.test.js'",
|
|
15
15
|
"test:coverage": "c8 npm test",
|
|
16
16
|
"test:coverage:report": "c8 --reporter=html npm test && echo 'Coverage report generated at coverage/index.html'",
|
|
17
|
+
"postinstall": "node scripts/fix-node-pty-permissions.js",
|
|
17
18
|
"start": "node cli/index.js",
|
|
18
19
|
"typecheck": "tsc --noEmit",
|
|
19
20
|
"lint": "eslint .",
|
|
@@ -81,6 +82,7 @@
|
|
|
81
82
|
"cluster-templates/",
|
|
82
83
|
"hooks/",
|
|
83
84
|
"docker/",
|
|
85
|
+
"scripts/",
|
|
84
86
|
"README.md",
|
|
85
87
|
"LICENSE",
|
|
86
88
|
"CHANGELOG.md"
|
|
@@ -94,7 +96,7 @@
|
|
|
94
96
|
"chalk": "^4.1.2",
|
|
95
97
|
"commander": "^14.0.2",
|
|
96
98
|
"md-to-pdf": "^5.2.5",
|
|
97
|
-
"node-pty": "^1.
|
|
99
|
+
"node-pty": "^1.1.0",
|
|
98
100
|
"omelette": "^0.4.17",
|
|
99
101
|
"pidusage": "^4.0.1",
|
|
100
102
|
"proper-lockfile": "^4.1.2"
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Fix node-pty spawn-helper permissions.
|
|
4
|
+
*
|
|
5
|
+
* node-pty prebuilds ship with spawn-helper lacking execute permission (mode 644).
|
|
6
|
+
* This causes "posix_spawnp failed" errors on macOS/Linux.
|
|
7
|
+
*
|
|
8
|
+
* Upstream bug: https://github.com/microsoft/node-pty/issues/XXX
|
|
9
|
+
* (File issue if not already reported)
|
|
10
|
+
*
|
|
11
|
+
* This script runs as postinstall to fix it automatically.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
|
|
17
|
+
// Skip on Windows - chmod doesn't apply
|
|
18
|
+
if (process.platform === 'win32') {
|
|
19
|
+
process.exit(0);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const prebuildsDir = path.join(__dirname, '..', 'node_modules', 'node-pty', 'prebuilds');
|
|
23
|
+
|
|
24
|
+
if (!fs.existsSync(prebuildsDir)) {
|
|
25
|
+
// node-pty not installed yet or using compiled version
|
|
26
|
+
process.exit(0);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let fixed = 0;
|
|
30
|
+
let errors = 0;
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const platforms = fs.readdirSync(prebuildsDir).filter(f => {
|
|
34
|
+
try {
|
|
35
|
+
return fs.statSync(path.join(prebuildsDir, f)).isDirectory();
|
|
36
|
+
} catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
for (const platform of platforms) {
|
|
42
|
+
// Only fix Unix platforms (darwin, linux)
|
|
43
|
+
if (!platform.startsWith('darwin') && !platform.startsWith('linux')) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const helper = path.join(prebuildsDir, platform, 'spawn-helper');
|
|
48
|
+
try {
|
|
49
|
+
if (!fs.existsSync(helper)) continue;
|
|
50
|
+
|
|
51
|
+
const stat = fs.statSync(helper);
|
|
52
|
+
// Check if not executable (missing user execute bit)
|
|
53
|
+
if (!(stat.mode & 0o100)) {
|
|
54
|
+
fs.chmodSync(helper, 0o755);
|
|
55
|
+
fixed++;
|
|
56
|
+
}
|
|
57
|
+
} catch (err) {
|
|
58
|
+
// Log but don't fail install - permission fix is best-effort
|
|
59
|
+
console.warn(`[postinstall] Warning: Could not fix ${helper}: ${err.message}`);
|
|
60
|
+
errors++;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
} catch (err) {
|
|
64
|
+
// Don't fail install on unexpected errors
|
|
65
|
+
console.warn(`[postinstall] Warning: node-pty permission fix failed: ${err.message}`);
|
|
66
|
+
process.exit(0);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (fixed > 0) {
|
|
70
|
+
console.log(`[postinstall] Fixed node-pty spawn-helper permissions (${fixed} platform(s))`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (errors > 0) {
|
|
74
|
+
console.warn(`[postinstall] ${errors} platform(s) could not be fixed - may need manual chmod`);
|
|
75
|
+
}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# record-demo.sh - Create ephemeral project, run zeroshot demo, record it, cleanup
|
|
3
|
+
#
|
|
4
|
+
# Usage:
|
|
5
|
+
# ./scripts/record-demo.sh # Interactive mode (you run zeroshot manually)
|
|
6
|
+
# ./scripts/record-demo.sh --record # Record with asciinema automatically
|
|
7
|
+
#
|
|
8
|
+
# Output: zeroshot-demo.cast (asciinema recording)
|
|
9
|
+
# Convert to gif: agg zeroshot-demo.cast zeroshot-demo.gif --idle-time-limit 2
|
|
10
|
+
|
|
11
|
+
set -e
|
|
12
|
+
|
|
13
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
14
|
+
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
15
|
+
DEMO_DIR=""
|
|
16
|
+
RECORD_MODE=false
|
|
17
|
+
FORCE_MODE=false
|
|
18
|
+
LOCK_FILE="/tmp/zeroshot-demo-recording.lock"
|
|
19
|
+
CAST_FILE="$REPO_ROOT/zeroshot-demo.cast"
|
|
20
|
+
|
|
21
|
+
# Check for existing recording session
|
|
22
|
+
check_existing_session() {
|
|
23
|
+
# Check lock file
|
|
24
|
+
if [[ -f "$LOCK_FILE" ]]; then
|
|
25
|
+
local old_pid
|
|
26
|
+
old_pid=$(cat "$LOCK_FILE" 2>/dev/null || echo "")
|
|
27
|
+
if [[ -n "$old_pid" ]] && kill -0 "$old_pid" 2>/dev/null; then
|
|
28
|
+
echo "ERROR: Recording already in progress (PID $old_pid)"
|
|
29
|
+
echo "Kill it with: kill $old_pid"
|
|
30
|
+
exit 1
|
|
31
|
+
fi
|
|
32
|
+
# Stale lock file
|
|
33
|
+
rm -f "$LOCK_FILE"
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
# Check for any asciinema processes recording to our file
|
|
37
|
+
local existing_asciinema
|
|
38
|
+
existing_asciinema=$(pgrep -f "asciinema.*zeroshot-demo.cast" 2>/dev/null || true)
|
|
39
|
+
if [[ -n "$existing_asciinema" ]]; then
|
|
40
|
+
echo "ERROR: Existing asciinema process(es) found: $existing_asciinema"
|
|
41
|
+
echo "Kill them with: pkill -f 'asciinema.*zeroshot-demo.cast'"
|
|
42
|
+
exit 1
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
# Check for any running zeroshot clusters
|
|
46
|
+
local existing_zeroshot
|
|
47
|
+
existing_zeroshot=$(pgrep -f "zeroshot.*rate limiting" 2>/dev/null || true)
|
|
48
|
+
if [[ -n "$existing_zeroshot" ]]; then
|
|
49
|
+
echo "ERROR: Existing zeroshot process(es) found: $existing_zeroshot"
|
|
50
|
+
echo "Kill them with: pkill -f 'zeroshot.*rate limiting'"
|
|
51
|
+
exit 1
|
|
52
|
+
fi
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
# Parse args
|
|
56
|
+
while [[ $# -gt 0 ]]; do
|
|
57
|
+
case "$1" in
|
|
58
|
+
--record) RECORD_MODE=true; shift ;;
|
|
59
|
+
--force|-f) FORCE_MODE=true; shift ;;
|
|
60
|
+
*) echo "Unknown option: $1"; exit 1 ;;
|
|
61
|
+
esac
|
|
62
|
+
done
|
|
63
|
+
|
|
64
|
+
# Cleanup on exit
|
|
65
|
+
cleanup() {
|
|
66
|
+
# Remove lock file
|
|
67
|
+
rm -f "$LOCK_FILE"
|
|
68
|
+
|
|
69
|
+
if [[ -n "$DEMO_DIR" && -d "$DEMO_DIR" ]]; then
|
|
70
|
+
echo ""
|
|
71
|
+
echo "Cleaning up $DEMO_DIR..."
|
|
72
|
+
rm -rf "$DEMO_DIR"
|
|
73
|
+
echo "Done."
|
|
74
|
+
fi
|
|
75
|
+
}
|
|
76
|
+
trap cleanup EXIT
|
|
77
|
+
|
|
78
|
+
# Check for conflicts before doing anything
|
|
79
|
+
check_existing_session
|
|
80
|
+
|
|
81
|
+
# Create temp project
|
|
82
|
+
DEMO_DIR=$(mktemp -d -t zeroshot-demo-XXXXXX)
|
|
83
|
+
echo "Creating demo project in $DEMO_DIR"
|
|
84
|
+
|
|
85
|
+
cd "$DEMO_DIR"
|
|
86
|
+
|
|
87
|
+
# Initialize package.json
|
|
88
|
+
cat > package.json << 'EOF'
|
|
89
|
+
{
|
|
90
|
+
"name": "demo-api",
|
|
91
|
+
"version": "1.0.0",
|
|
92
|
+
"type": "commonjs",
|
|
93
|
+
"scripts": {
|
|
94
|
+
"dev": "ts-node src/index.ts",
|
|
95
|
+
"build": "tsc",
|
|
96
|
+
"start": "node dist/index.js"
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
EOF
|
|
100
|
+
|
|
101
|
+
# Install dependencies (quiet)
|
|
102
|
+
echo "Installing dependencies..."
|
|
103
|
+
npm install --silent express typescript ts-node @types/express @types/node
|
|
104
|
+
|
|
105
|
+
# Create tsconfig
|
|
106
|
+
cat > tsconfig.json << 'EOF'
|
|
107
|
+
{
|
|
108
|
+
"compilerOptions": {
|
|
109
|
+
"target": "ES2020",
|
|
110
|
+
"module": "commonjs",
|
|
111
|
+
"strict": true,
|
|
112
|
+
"esModuleInterop": true,
|
|
113
|
+
"outDir": "dist",
|
|
114
|
+
"rootDir": "src"
|
|
115
|
+
},
|
|
116
|
+
"include": ["src/**/*"]
|
|
117
|
+
}
|
|
118
|
+
EOF
|
|
119
|
+
|
|
120
|
+
# Create minimal server with users "database"
|
|
121
|
+
mkdir -p src
|
|
122
|
+
|
|
123
|
+
cat > src/db.ts << 'EOF'
|
|
124
|
+
// Simple in-memory database
|
|
125
|
+
export interface User {
|
|
126
|
+
id: number;
|
|
127
|
+
username: string;
|
|
128
|
+
email: string;
|
|
129
|
+
password_hash: string;
|
|
130
|
+
avatar: string;
|
|
131
|
+
created_at: Date;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export const users: User[] = [
|
|
135
|
+
{
|
|
136
|
+
id: 1,
|
|
137
|
+
username: "alice",
|
|
138
|
+
email: "alice@example.com",
|
|
139
|
+
password_hash: "$2b$10$X7VYKzPQ...",
|
|
140
|
+
avatar: "https://example.com/alice.jpg",
|
|
141
|
+
created_at: new Date("2024-01-15"),
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
id: 2,
|
|
145
|
+
username: "bob",
|
|
146
|
+
email: "bob@example.com",
|
|
147
|
+
password_hash: "$2b$10$Y8WZLaQR...",
|
|
148
|
+
avatar: "https://example.com/bob.jpg",
|
|
149
|
+
created_at: new Date("2024-02-20"),
|
|
150
|
+
},
|
|
151
|
+
];
|
|
152
|
+
EOF
|
|
153
|
+
|
|
154
|
+
cat > src/index.ts << 'EOF'
|
|
155
|
+
import express from "express";
|
|
156
|
+
|
|
157
|
+
const app = express();
|
|
158
|
+
const PORT = 3000;
|
|
159
|
+
|
|
160
|
+
app.use(express.json());
|
|
161
|
+
|
|
162
|
+
// Health check
|
|
163
|
+
app.get("/health", (_, res) => {
|
|
164
|
+
res.json({ status: "ok" });
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
app.listen(PORT, () => {
|
|
168
|
+
console.log(`Server running on http://localhost:${PORT}`);
|
|
169
|
+
});
|
|
170
|
+
EOF
|
|
171
|
+
|
|
172
|
+
# Initialize git repo
|
|
173
|
+
git init --quiet
|
|
174
|
+
git add -A
|
|
175
|
+
git commit --quiet -m "Initial commit: Express API with users database"
|
|
176
|
+
|
|
177
|
+
echo ""
|
|
178
|
+
echo "=========================================="
|
|
179
|
+
echo "Demo project ready!"
|
|
180
|
+
echo "=========================================="
|
|
181
|
+
echo ""
|
|
182
|
+
echo "Directory: $DEMO_DIR"
|
|
183
|
+
echo ""
|
|
184
|
+
echo "Suggested demo task:"
|
|
185
|
+
echo " zeroshot 'Add PUT /users/:id endpoint to update user profile'"
|
|
186
|
+
echo ""
|
|
187
|
+
|
|
188
|
+
if [[ "$RECORD_MODE" == "true" ]]; then
|
|
189
|
+
echo "Recording with asciinema..."
|
|
190
|
+
echo ""
|
|
191
|
+
|
|
192
|
+
# Check asciinema is installed
|
|
193
|
+
if ! command -v asciinema &> /dev/null; then
|
|
194
|
+
echo "Error: asciinema not installed. Run: pip install asciinema"
|
|
195
|
+
exit 1
|
|
196
|
+
fi
|
|
197
|
+
|
|
198
|
+
# Warn if cast file exists (skip with --force)
|
|
199
|
+
if [[ -f "$CAST_FILE" ]] && [[ "$FORCE_MODE" != "true" ]]; then
|
|
200
|
+
echo "WARNING: $CAST_FILE already exists!"
|
|
201
|
+
echo "Previous recording will be OVERWRITTEN."
|
|
202
|
+
echo ""
|
|
203
|
+
read -p "Continue? [y/N] " -n 1 -r
|
|
204
|
+
echo ""
|
|
205
|
+
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
206
|
+
echo "Aborted."
|
|
207
|
+
exit 1
|
|
208
|
+
fi
|
|
209
|
+
fi
|
|
210
|
+
|
|
211
|
+
# Create lock file with our PID
|
|
212
|
+
echo $$ > "$LOCK_FILE"
|
|
213
|
+
|
|
214
|
+
# Record the session
|
|
215
|
+
# CRITICAL: Use bash -l -c to get full PATH (includes npm global binaries)
|
|
216
|
+
ZEROSHOT_PATH=$(which zeroshot)
|
|
217
|
+
TASK="Add rate limiting middleware: sliding window algorithm (not fixed window), per-IP tracking with in-memory store and automatic TTL cleanup to prevent memory leaks, configurable limits per endpoint. Return 429 Too Many Requests with Retry-After header (seconds until reset) and X-RateLimit-Remaining header on ALL responses. Must handle both IPv4 and IPv6, normalizing IPv6 to consistent format."
|
|
218
|
+
|
|
219
|
+
# SIGNAL ISOLATION: Use setsid to create new session, completely immune to terminal signals
|
|
220
|
+
# The recording process will NOT be killed by Ctrl+C or signals to this script
|
|
221
|
+
# We wait for it explicitly and only cleanup AFTER it naturally completes
|
|
222
|
+
echo "Starting recording in isolated session (immune to Ctrl+C)..."
|
|
223
|
+
echo "To kill it manually: pkill -f 'asciinema.*zeroshot-demo.cast'"
|
|
224
|
+
echo ""
|
|
225
|
+
|
|
226
|
+
# Disable cleanup trap during recording - we'll handle it manually
|
|
227
|
+
trap - EXIT
|
|
228
|
+
|
|
229
|
+
# Start asciinema in new session (setsid) so it's immune to our signals
|
|
230
|
+
# Save the session leader PID so we can wait for it
|
|
231
|
+
setsid bash -c "
|
|
232
|
+
# Ignore all signals - this recording WILL NOT DIE
|
|
233
|
+
trap '' INT TERM HUP QUIT
|
|
234
|
+
|
|
235
|
+
cd '$DEMO_DIR'
|
|
236
|
+
asciinema rec \
|
|
237
|
+
--overwrite \
|
|
238
|
+
--title 'Zeroshot Demo' \
|
|
239
|
+
--command \"bash -l -c '$ZEROSHOT_PATH \\\"$TASK\\\"'\" \
|
|
240
|
+
'$CAST_FILE'
|
|
241
|
+
" &
|
|
242
|
+
RECORDING_PID=$!
|
|
243
|
+
|
|
244
|
+
# Update lock file with the actual recording PID
|
|
245
|
+
echo $RECORDING_PID > "$LOCK_FILE"
|
|
246
|
+
|
|
247
|
+
echo "Recording started (session PID: $RECORDING_PID)"
|
|
248
|
+
echo "Waiting for recording to complete..."
|
|
249
|
+
echo ""
|
|
250
|
+
|
|
251
|
+
# Wait for recording to finish - this script will NOT kill it on Ctrl+C
|
|
252
|
+
# Ignore signals while waiting
|
|
253
|
+
trap '' INT TERM HUP
|
|
254
|
+
wait $RECORDING_PID 2>/dev/null || true
|
|
255
|
+
|
|
256
|
+
# Recording finished naturally - now cleanup
|
|
257
|
+
echo ""
|
|
258
|
+
echo "Recording saved to: $CAST_FILE"
|
|
259
|
+
echo ""
|
|
260
|
+
echo "Convert to gif with:"
|
|
261
|
+
echo " agg $CAST_FILE $REPO_ROOT/zeroshot-demo.gif --idle-time-limit 2"
|
|
262
|
+
|
|
263
|
+
# Now do cleanup
|
|
264
|
+
rm -f "$LOCK_FILE"
|
|
265
|
+
if [[ -n "$DEMO_DIR" && -d "$DEMO_DIR" ]]; then
|
|
266
|
+
echo ""
|
|
267
|
+
echo "Cleaning up $DEMO_DIR..."
|
|
268
|
+
rm -rf "$DEMO_DIR"
|
|
269
|
+
echo "Done."
|
|
270
|
+
fi
|
|
271
|
+
else
|
|
272
|
+
echo "Interactive mode - run zeroshot manually:"
|
|
273
|
+
echo ""
|
|
274
|
+
echo " cd $DEMO_DIR"
|
|
275
|
+
echo " zeroshot 'Add PUT /users/:id endpoint to update user profile'"
|
|
276
|
+
echo ""
|
|
277
|
+
echo "Press Enter when done to cleanup..."
|
|
278
|
+
read -r
|
|
279
|
+
fi
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Test zeroshot installation on any platform
|
|
3
|
+
# Run: ./scripts/test-install.sh
|
|
4
|
+
|
|
5
|
+
set -e
|
|
6
|
+
|
|
7
|
+
echo "=== Zeroshot Install Test ==="
|
|
8
|
+
echo "Platform: $(uname -s) $(uname -m)"
|
|
9
|
+
echo "Node: $(node --version)"
|
|
10
|
+
echo "npm: $(npm --version)"
|
|
11
|
+
echo ""
|
|
12
|
+
|
|
13
|
+
echo "1. Installing dependencies..."
|
|
14
|
+
npm install
|
|
15
|
+
|
|
16
|
+
echo ""
|
|
17
|
+
echo "2. Linking CLI..."
|
|
18
|
+
npm link
|
|
19
|
+
|
|
20
|
+
echo ""
|
|
21
|
+
echo "3. Testing CLI commands..."
|
|
22
|
+
|
|
23
|
+
echo " zeroshot --version"
|
|
24
|
+
zeroshot --version
|
|
25
|
+
|
|
26
|
+
echo ""
|
|
27
|
+
echo " zeroshot --help"
|
|
28
|
+
zeroshot --help | head -20
|
|
29
|
+
|
|
30
|
+
echo ""
|
|
31
|
+
echo " zeroshot list"
|
|
32
|
+
zeroshot list 2>/dev/null || echo " (no clusters yet - this is expected)"
|
|
33
|
+
|
|
34
|
+
echo ""
|
|
35
|
+
echo " zeroshot config list"
|
|
36
|
+
zeroshot config list
|
|
37
|
+
|
|
38
|
+
echo ""
|
|
39
|
+
echo "=== ALL TESTS PASSED ==="
|
|
40
|
+
echo "Zeroshot installed successfully on $(uname -s)!"
|
package/src/preflight.js
CHANGED
|
@@ -84,9 +84,31 @@ function getClaudeVersion() {
|
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
/**
|
|
88
|
+
* Check macOS Keychain for Claude Code credentials
|
|
89
|
+
* @returns {{ authenticated: boolean, error: string | null }}
|
|
90
|
+
*/
|
|
91
|
+
function checkMacOsKeychain() {
|
|
92
|
+
if (os.platform() !== 'darwin') {
|
|
93
|
+
return { authenticated: false, error: 'Not macOS' };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
// Check if Claude Code credentials exist in Keychain
|
|
98
|
+
execSync('security find-generic-password -s "Claude Code-credentials"', {
|
|
99
|
+
encoding: 'utf8',
|
|
100
|
+
stdio: 'pipe',
|
|
101
|
+
timeout: 2000,
|
|
102
|
+
});
|
|
103
|
+
return { authenticated: true, error: null };
|
|
104
|
+
} catch {
|
|
105
|
+
return { authenticated: false, error: 'No credentials in Keychain' };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
87
109
|
/**
|
|
88
110
|
* Check Claude CLI authentication status
|
|
89
|
-
* @returns {{ authenticated: boolean, error: string | null, configDir: string }}
|
|
111
|
+
* @returns {{ authenticated: boolean, error: string | null, configDir: string, method?: string }}
|
|
90
112
|
*/
|
|
91
113
|
function checkClaudeAuth() {
|
|
92
114
|
const configDir = process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude');
|
|
@@ -94,6 +116,20 @@ function checkClaudeAuth() {
|
|
|
94
116
|
|
|
95
117
|
// Check if credentials file exists
|
|
96
118
|
if (!fs.existsSync(credentialsPath)) {
|
|
119
|
+
// No credentials file - check macOS Keychain as fallback
|
|
120
|
+
// Only use Keychain when using default config dir (not custom CLAUDE_CONFIG_DIR)
|
|
121
|
+
const isDefaultConfigDir = !process.env.CLAUDE_CONFIG_DIR;
|
|
122
|
+
if (isDefaultConfigDir) {
|
|
123
|
+
const keychainResult = checkMacOsKeychain();
|
|
124
|
+
if (keychainResult.authenticated) {
|
|
125
|
+
return {
|
|
126
|
+
authenticated: true,
|
|
127
|
+
error: null,
|
|
128
|
+
configDir,
|
|
129
|
+
method: 'keychain',
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
}
|
|
97
133
|
return {
|
|
98
134
|
authenticated: false,
|
|
99
135
|
error: 'No credentials file found',
|
|
@@ -38,7 +38,7 @@ export function listTasks(options = {}) {
|
|
|
38
38
|
|
|
39
39
|
const statusColor =
|
|
40
40
|
{
|
|
41
|
-
running: chalk.
|
|
41
|
+
running: chalk.green,
|
|
42
42
|
completed: chalk.green,
|
|
43
43
|
failed: chalk.red,
|
|
44
44
|
stale: chalk.yellow,
|
|
@@ -75,7 +75,7 @@ export function listTasks(options = {}) {
|
|
|
75
75
|
|
|
76
76
|
const statusColor =
|
|
77
77
|
{
|
|
78
|
-
running: chalk.
|
|
78
|
+
running: chalk.green,
|
|
79
79
|
completed: chalk.green,
|
|
80
80
|
failed: chalk.red,
|
|
81
81
|
stale: chalk.yellow,
|
package/task-lib/tui.js
CHANGED
|
@@ -283,7 +283,7 @@ class TaskTUI {
|
|
|
283
283
|
const items = this.tasks.map((task) => {
|
|
284
284
|
const statusIcon =
|
|
285
285
|
{
|
|
286
|
-
running: '{
|
|
286
|
+
running: '{green-fg}●{/}',
|
|
287
287
|
completed: '{green-fg}●{/}',
|
|
288
288
|
failed: '{red-fg}●{/}',
|
|
289
289
|
stale: '{yellow-fg}●{/}',
|
|
@@ -372,7 +372,7 @@ class TaskTUI {
|
|
|
372
372
|
|
|
373
373
|
getStatusColor(status) {
|
|
374
374
|
const colors = {
|
|
375
|
-
running: '{
|
|
375
|
+
running: '{green-fg}running{/}',
|
|
376
376
|
completed: '{green-fg}completed{/}',
|
|
377
377
|
failed: '{red-fg}failed{/}',
|
|
378
378
|
stale: '{yellow-fg}stale{/}',
|