@heylemon/lemonade 0.6.7 → 0.7.0
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/dist/agents/model-fallback.js +7 -2
- package/dist/agents/pi-tool-definition-adapter.js +83 -10
- package/dist/agents/system-prompt.js +20 -1
- package/dist/auto-reply/reply/session.js +34 -8
- package/dist/build-info.json +3 -3
- package/dist/canvas-host/a2ui/.bundle.hash +1 -0
- package/dist/canvas-host/a2ui/a2ui.bundle.js +17775 -0
- package/dist/canvas-host/a2ui/index.html +307 -0
- package/dist/control-ui/assets/{index-0MsyRFLD.js → index-D5pbk0Cx.js} +261 -261
- package/dist/control-ui/assets/index-D5pbk0Cx.js.map +1 -0
- package/dist/control-ui/index.html +1 -1
- package/dist/gateway/auth-rate-limit.js +121 -0
- package/dist/gateway/auth.js +26 -8
- package/dist/gateway/server-http.js +7 -4
- package/dist/gateway/session-utils.fs.js +18 -0
- package/dist/gateway/skills-http.js +65 -0
- package/dist/security/secret-equal.js +13 -0
- package/dist/web/auto-reply/monitor.js +10 -2
- package/dist/web/login-qr.js +12 -0
- package/extensions/diagnostics-otel/node_modules/.bin/acorn +2 -2
- package/extensions/googlechat/node_modules/.bin/lemonade +2 -2
- package/extensions/line/node_modules/.bin/lemonade +2 -2
- package/extensions/matrix/node_modules/.bin/lemonade +2 -2
- package/extensions/matrix/node_modules/.bin/markdown-it +2 -2
- package/extensions/memory-core/node_modules/.bin/lemonade +2 -2
- package/extensions/memory-lancedb/node_modules/.bin/arrow2csv +2 -2
- package/extensions/memory-lancedb/node_modules/.bin/openai +2 -2
- package/extensions/msteams/node_modules/.bin/lemonade +2 -2
- package/extensions/nostr/node_modules/.bin/lemonade +2 -2
- package/extensions/nostr/node_modules/.bin/tsc +2 -2
- package/extensions/nostr/node_modules/.bin/tsserver +2 -2
- package/extensions/twitch/node_modules/.bin/lemonade +2 -2
- package/extensions/zalo/node_modules/.bin/lemonade +2 -2
- package/extensions/zalouser/node_modules/.bin/lemonade +2 -2
- package/package.json +1 -1
- package/skills/sherpa-onnx-tts/bin/sherpa-onnx-tts +178 -0
- package/skills/travel-agent-persona/SKILL.md +89 -0
- package/dist/control-ui/assets/index-0MsyRFLD.js.map +0 -1
- package/extensions/googlechat/node_modules/.bin/lemon-calendar +0 -21
- package/extensions/googlechat/node_modules/.bin/lemon-cron +0 -21
- package/extensions/googlechat/node_modules/.bin/lemon-docs +0 -21
- package/extensions/googlechat/node_modules/.bin/lemon-drive +0 -21
- package/extensions/googlechat/node_modules/.bin/lemon-gmail +0 -21
- package/extensions/googlechat/node_modules/.bin/lemon-jira +0 -21
- package/extensions/googlechat/node_modules/.bin/lemon-notion +0 -21
- package/extensions/googlechat/node_modules/.bin/lemon-sheets +0 -21
- package/extensions/googlechat/node_modules/.bin/lemon-slack +0 -21
- package/extensions/googlechat/node_modules/.bin/lemon-slides +0 -21
- package/extensions/googlechat/node_modules/.bin/lemon-twitter +0 -21
- package/extensions/googlechat/node_modules/.bin/lemon-youtube +0 -21
- package/extensions/line/node_modules/.bin/lemon-calendar +0 -21
- package/extensions/line/node_modules/.bin/lemon-cron +0 -21
- package/extensions/line/node_modules/.bin/lemon-docs +0 -21
- package/extensions/line/node_modules/.bin/lemon-drive +0 -21
- package/extensions/line/node_modules/.bin/lemon-gmail +0 -21
- package/extensions/line/node_modules/.bin/lemon-jira +0 -21
- package/extensions/line/node_modules/.bin/lemon-notion +0 -21
- package/extensions/line/node_modules/.bin/lemon-sheets +0 -21
- package/extensions/line/node_modules/.bin/lemon-slack +0 -21
- package/extensions/line/node_modules/.bin/lemon-slides +0 -21
- package/extensions/line/node_modules/.bin/lemon-twitter +0 -21
- package/extensions/line/node_modules/.bin/lemon-youtube +0 -21
- package/extensions/matrix/node_modules/.bin/lemon-calendar +0 -21
- package/extensions/matrix/node_modules/.bin/lemon-cron +0 -21
- package/extensions/matrix/node_modules/.bin/lemon-docs +0 -21
- package/extensions/matrix/node_modules/.bin/lemon-drive +0 -21
- package/extensions/matrix/node_modules/.bin/lemon-gmail +0 -21
- package/extensions/matrix/node_modules/.bin/lemon-jira +0 -21
- package/extensions/matrix/node_modules/.bin/lemon-notion +0 -21
- package/extensions/matrix/node_modules/.bin/lemon-sheets +0 -21
- package/extensions/matrix/node_modules/.bin/lemon-slack +0 -21
- package/extensions/matrix/node_modules/.bin/lemon-slides +0 -21
- package/extensions/matrix/node_modules/.bin/lemon-twitter +0 -21
- package/extensions/matrix/node_modules/.bin/lemon-youtube +0 -21
- package/extensions/memory-core/node_modules/.bin/lemon-calendar +0 -21
- package/extensions/memory-core/node_modules/.bin/lemon-cron +0 -21
- package/extensions/memory-core/node_modules/.bin/lemon-docs +0 -21
- package/extensions/memory-core/node_modules/.bin/lemon-drive +0 -21
- package/extensions/memory-core/node_modules/.bin/lemon-gmail +0 -21
- package/extensions/memory-core/node_modules/.bin/lemon-jira +0 -21
- package/extensions/memory-core/node_modules/.bin/lemon-notion +0 -21
- package/extensions/memory-core/node_modules/.bin/lemon-sheets +0 -21
- package/extensions/memory-core/node_modules/.bin/lemon-slack +0 -21
- package/extensions/memory-core/node_modules/.bin/lemon-slides +0 -21
- package/extensions/memory-core/node_modules/.bin/lemon-twitter +0 -21
- package/extensions/memory-core/node_modules/.bin/lemon-youtube +0 -21
- package/extensions/msteams/node_modules/.bin/lemon-calendar +0 -21
- package/extensions/msteams/node_modules/.bin/lemon-cron +0 -21
- package/extensions/msteams/node_modules/.bin/lemon-docs +0 -21
- package/extensions/msteams/node_modules/.bin/lemon-drive +0 -21
- package/extensions/msteams/node_modules/.bin/lemon-gmail +0 -21
- package/extensions/msteams/node_modules/.bin/lemon-jira +0 -21
- package/extensions/msteams/node_modules/.bin/lemon-notion +0 -21
- package/extensions/msteams/node_modules/.bin/lemon-sheets +0 -21
- package/extensions/msteams/node_modules/.bin/lemon-slack +0 -21
- package/extensions/msteams/node_modules/.bin/lemon-slides +0 -21
- package/extensions/msteams/node_modules/.bin/lemon-twitter +0 -21
- package/extensions/msteams/node_modules/.bin/lemon-youtube +0 -21
- package/extensions/nostr/node_modules/.bin/lemon-calendar +0 -21
- package/extensions/nostr/node_modules/.bin/lemon-cron +0 -21
- package/extensions/nostr/node_modules/.bin/lemon-docs +0 -21
- package/extensions/nostr/node_modules/.bin/lemon-drive +0 -21
- package/extensions/nostr/node_modules/.bin/lemon-gmail +0 -21
- package/extensions/nostr/node_modules/.bin/lemon-jira +0 -21
- package/extensions/nostr/node_modules/.bin/lemon-notion +0 -21
- package/extensions/nostr/node_modules/.bin/lemon-sheets +0 -21
- package/extensions/nostr/node_modules/.bin/lemon-slack +0 -21
- package/extensions/nostr/node_modules/.bin/lemon-slides +0 -21
- package/extensions/nostr/node_modules/.bin/lemon-twitter +0 -21
- package/extensions/nostr/node_modules/.bin/lemon-youtube +0 -21
- package/extensions/twitch/node_modules/.bin/lemon-calendar +0 -21
- package/extensions/twitch/node_modules/.bin/lemon-cron +0 -21
- package/extensions/twitch/node_modules/.bin/lemon-docs +0 -21
- package/extensions/twitch/node_modules/.bin/lemon-drive +0 -21
- package/extensions/twitch/node_modules/.bin/lemon-gmail +0 -21
- package/extensions/twitch/node_modules/.bin/lemon-jira +0 -21
- package/extensions/twitch/node_modules/.bin/lemon-notion +0 -21
- package/extensions/twitch/node_modules/.bin/lemon-sheets +0 -21
- package/extensions/twitch/node_modules/.bin/lemon-slack +0 -21
- package/extensions/twitch/node_modules/.bin/lemon-slides +0 -21
- package/extensions/twitch/node_modules/.bin/lemon-twitter +0 -21
- package/extensions/twitch/node_modules/.bin/lemon-youtube +0 -21
- package/extensions/zalo/node_modules/.bin/lemon-calendar +0 -21
- package/extensions/zalo/node_modules/.bin/lemon-cron +0 -21
- package/extensions/zalo/node_modules/.bin/lemon-docs +0 -21
- package/extensions/zalo/node_modules/.bin/lemon-drive +0 -21
- package/extensions/zalo/node_modules/.bin/lemon-gmail +0 -21
- package/extensions/zalo/node_modules/.bin/lemon-jira +0 -21
- package/extensions/zalo/node_modules/.bin/lemon-notion +0 -21
- package/extensions/zalo/node_modules/.bin/lemon-sheets +0 -21
- package/extensions/zalo/node_modules/.bin/lemon-slack +0 -21
- package/extensions/zalo/node_modules/.bin/lemon-slides +0 -21
- package/extensions/zalo/node_modules/.bin/lemon-twitter +0 -21
- package/extensions/zalo/node_modules/.bin/lemon-youtube +0 -21
- package/extensions/zalouser/node_modules/.bin/lemon-calendar +0 -21
- package/extensions/zalouser/node_modules/.bin/lemon-cron +0 -21
- package/extensions/zalouser/node_modules/.bin/lemon-docs +0 -21
- package/extensions/zalouser/node_modules/.bin/lemon-drive +0 -21
- package/extensions/zalouser/node_modules/.bin/lemon-gmail +0 -21
- package/extensions/zalouser/node_modules/.bin/lemon-jira +0 -21
- package/extensions/zalouser/node_modules/.bin/lemon-notion +0 -21
- package/extensions/zalouser/node_modules/.bin/lemon-sheets +0 -21
- package/extensions/zalouser/node_modules/.bin/lemon-slack +0 -21
- package/extensions/zalouser/node_modules/.bin/lemon-slides +0 -21
- package/extensions/zalouser/node_modules/.bin/lemon-twitter +0 -21
- package/extensions/zalouser/node_modules/.bin/lemon-youtube +0 -21
- /package/skills/pdf/{FORMS.md → forms.md} +0 -0
- /package/skills/pdf/{REFERENCE.md → reference.md} +0 -0
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<link rel="icon" type="image/svg+xml" href="./favicon.svg" />
|
|
9
9
|
<link rel="icon" type="image/png" sizes="32x32" href="./favicon-32.png" />
|
|
10
10
|
<link rel="apple-touch-icon" sizes="180x180" href="./apple-touch-icon.png" />
|
|
11
|
-
<script type="module" crossorigin src="./assets/index-
|
|
11
|
+
<script type="module" crossorigin src="./assets/index-D5pbk0Cx.js"></script>
|
|
12
12
|
<link rel="stylesheet" crossorigin href="./assets/index-BKPyesll.css">
|
|
13
13
|
</head>
|
|
14
14
|
<body>
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory sliding-window rate limiter for gateway authentication attempts.
|
|
3
|
+
*
|
|
4
|
+
* Tracks failed auth attempts by {scope, clientIp}. A scope lets callers keep
|
|
5
|
+
* independent counters for different credential classes (e.g. shared gateway
|
|
6
|
+
* token/password vs device-token auth) while sharing one limiter instance.
|
|
7
|
+
*
|
|
8
|
+
* Loopback addresses (127.0.0.1 / ::1) are exempt by default so that local
|
|
9
|
+
* CLI sessions and the native macOS app are never locked out.
|
|
10
|
+
*/
|
|
11
|
+
import { isLoopbackAddress, resolveGatewayClientIp } from "./net.js";
|
|
12
|
+
export const AUTH_RATE_LIMIT_SCOPE_DEFAULT = "default";
|
|
13
|
+
export const AUTH_RATE_LIMIT_SCOPE_SHARED_SECRET = "shared-secret";
|
|
14
|
+
export const AUTH_RATE_LIMIT_SCOPE_DEVICE_TOKEN = "device-token";
|
|
15
|
+
export const AUTH_RATE_LIMIT_SCOPE_HOOK_AUTH = "hook-auth";
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// Defaults
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
const DEFAULT_MAX_ATTEMPTS = 10;
|
|
20
|
+
const DEFAULT_WINDOW_MS = 60_000;
|
|
21
|
+
const DEFAULT_LOCKOUT_MS = 300_000;
|
|
22
|
+
const PRUNE_INTERVAL_MS = 60_000;
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Implementation
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
export function normalizeRateLimitClientIp(ip) {
|
|
27
|
+
return resolveGatewayClientIp({ remoteAddr: ip ?? "" }) ?? "unknown";
|
|
28
|
+
}
|
|
29
|
+
export function createAuthRateLimiter(config) {
|
|
30
|
+
const maxAttempts = config?.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
|
|
31
|
+
const windowMs = config?.windowMs ?? DEFAULT_WINDOW_MS;
|
|
32
|
+
const lockoutMs = config?.lockoutMs ?? DEFAULT_LOCKOUT_MS;
|
|
33
|
+
const exemptLoopback = config?.exemptLoopback ?? true;
|
|
34
|
+
const pruneIntervalMs = config?.pruneIntervalMs ?? PRUNE_INTERVAL_MS;
|
|
35
|
+
const entries = new Map();
|
|
36
|
+
const pruneTimer = pruneIntervalMs > 0 ? setInterval(() => prune(), pruneIntervalMs) : null;
|
|
37
|
+
if (pruneTimer?.unref) {
|
|
38
|
+
pruneTimer.unref();
|
|
39
|
+
}
|
|
40
|
+
function normalizeScope(scope) {
|
|
41
|
+
return (scope ?? AUTH_RATE_LIMIT_SCOPE_DEFAULT).trim() || AUTH_RATE_LIMIT_SCOPE_DEFAULT;
|
|
42
|
+
}
|
|
43
|
+
function normalizeIp(ip) {
|
|
44
|
+
return normalizeRateLimitClientIp(ip);
|
|
45
|
+
}
|
|
46
|
+
function resolveKey(rawIp, rawScope) {
|
|
47
|
+
const ip = normalizeIp(rawIp);
|
|
48
|
+
const scope = normalizeScope(rawScope);
|
|
49
|
+
return { key: `${scope}:${ip}`, ip };
|
|
50
|
+
}
|
|
51
|
+
function isExempt(ip) {
|
|
52
|
+
return exemptLoopback && isLoopbackAddress(ip);
|
|
53
|
+
}
|
|
54
|
+
function slideWindow(entry, now) {
|
|
55
|
+
const cutoff = now - windowMs;
|
|
56
|
+
entry.attempts = entry.attempts.filter((ts) => ts > cutoff);
|
|
57
|
+
}
|
|
58
|
+
function check(rawIp, rawScope) {
|
|
59
|
+
const { key, ip } = resolveKey(rawIp, rawScope);
|
|
60
|
+
if (isExempt(ip)) {
|
|
61
|
+
return { allowed: true, remaining: maxAttempts, retryAfterMs: 0 };
|
|
62
|
+
}
|
|
63
|
+
const now = Date.now();
|
|
64
|
+
const entry = entries.get(key);
|
|
65
|
+
if (!entry) {
|
|
66
|
+
return { allowed: true, remaining: maxAttempts, retryAfterMs: 0 };
|
|
67
|
+
}
|
|
68
|
+
if (entry.lockedUntil && now < entry.lockedUntil) {
|
|
69
|
+
return { allowed: false, remaining: 0, retryAfterMs: entry.lockedUntil - now };
|
|
70
|
+
}
|
|
71
|
+
if (entry.lockedUntil && now >= entry.lockedUntil) {
|
|
72
|
+
entry.lockedUntil = undefined;
|
|
73
|
+
entry.attempts = [];
|
|
74
|
+
}
|
|
75
|
+
slideWindow(entry, now);
|
|
76
|
+
const remaining = Math.max(0, maxAttempts - entry.attempts.length);
|
|
77
|
+
return { allowed: remaining > 0, remaining, retryAfterMs: 0 };
|
|
78
|
+
}
|
|
79
|
+
function recordFailure(rawIp, rawScope) {
|
|
80
|
+
const { key, ip } = resolveKey(rawIp, rawScope);
|
|
81
|
+
if (isExempt(ip))
|
|
82
|
+
return;
|
|
83
|
+
const now = Date.now();
|
|
84
|
+
let entry = entries.get(key);
|
|
85
|
+
if (!entry) {
|
|
86
|
+
entry = { attempts: [] };
|
|
87
|
+
entries.set(key, entry);
|
|
88
|
+
}
|
|
89
|
+
if (entry.lockedUntil && now < entry.lockedUntil)
|
|
90
|
+
return;
|
|
91
|
+
slideWindow(entry, now);
|
|
92
|
+
entry.attempts.push(now);
|
|
93
|
+
if (entry.attempts.length >= maxAttempts) {
|
|
94
|
+
entry.lockedUntil = now + lockoutMs;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function reset(rawIp, rawScope) {
|
|
98
|
+
const { key } = resolveKey(rawIp, rawScope);
|
|
99
|
+
entries.delete(key);
|
|
100
|
+
}
|
|
101
|
+
function prune() {
|
|
102
|
+
const now = Date.now();
|
|
103
|
+
for (const [key, entry] of entries) {
|
|
104
|
+
if (entry.lockedUntil && now < entry.lockedUntil)
|
|
105
|
+
continue;
|
|
106
|
+
slideWindow(entry, now);
|
|
107
|
+
if (entry.attempts.length === 0) {
|
|
108
|
+
entries.delete(key);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function size() {
|
|
113
|
+
return entries.size;
|
|
114
|
+
}
|
|
115
|
+
function dispose() {
|
|
116
|
+
if (pruneTimer)
|
|
117
|
+
clearInterval(pruneTimer);
|
|
118
|
+
entries.clear();
|
|
119
|
+
}
|
|
120
|
+
return { check, recordFailure, reset, size, prune, dispose };
|
|
121
|
+
}
|
package/dist/gateway/auth.js
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
import { timingSafeEqual } from "node:crypto";
|
|
2
1
|
import { readTailscaleWhoisIdentity } from "../infra/tailscale.js";
|
|
2
|
+
import { safeEqualSecret } from "../security/secret-equal.js";
|
|
3
|
+
import { AUTH_RATE_LIMIT_SCOPE_SHARED_SECRET, } from "./auth-rate-limit.js";
|
|
3
4
|
import { isTrustedProxyAddress, parseForwardedForClientIp, resolveGatewayClientIp } from "./net.js";
|
|
4
|
-
function safeEqual(a, b) {
|
|
5
|
-
if (a.length !== b.length)
|
|
6
|
-
return false;
|
|
7
|
-
return timingSafeEqual(Buffer.from(a), Buffer.from(b));
|
|
8
|
-
}
|
|
9
5
|
function normalizeLogin(login) {
|
|
10
6
|
return login.trim().toLowerCase();
|
|
11
7
|
}
|
|
@@ -155,12 +151,27 @@ export async function authorizeGatewayConnect(params) {
|
|
|
155
151
|
const { auth, connectAuth, req, trustedProxies } = params;
|
|
156
152
|
const tailscaleWhois = params.tailscaleWhois ?? readTailscaleWhoisIdentity;
|
|
157
153
|
const localDirect = isLocalDirectRequest(req, trustedProxies);
|
|
154
|
+
const limiter = params.rateLimiter;
|
|
155
|
+
const ip = params.clientIp ?? resolveRequestClientIp(req, trustedProxies) ?? req?.socket?.remoteAddress;
|
|
156
|
+
const rateLimitScope = params.rateLimitScope ?? AUTH_RATE_LIMIT_SCOPE_SHARED_SECRET;
|
|
157
|
+
if (limiter) {
|
|
158
|
+
const rlCheck = limiter.check(ip, rateLimitScope);
|
|
159
|
+
if (!rlCheck.allowed) {
|
|
160
|
+
return {
|
|
161
|
+
ok: false,
|
|
162
|
+
reason: "rate_limited",
|
|
163
|
+
rateLimited: true,
|
|
164
|
+
retryAfterMs: rlCheck.retryAfterMs,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
}
|
|
158
168
|
if (auth.allowTailscale && !localDirect) {
|
|
159
169
|
const tailscaleCheck = await resolveVerifiedTailscaleUser({
|
|
160
170
|
req,
|
|
161
171
|
tailscaleWhois,
|
|
162
172
|
});
|
|
163
173
|
if (tailscaleCheck.ok) {
|
|
174
|
+
limiter?.reset(ip, rateLimitScope);
|
|
164
175
|
return {
|
|
165
176
|
ok: true,
|
|
166
177
|
method: "tailscale",
|
|
@@ -173,11 +184,14 @@ export async function authorizeGatewayConnect(params) {
|
|
|
173
184
|
return { ok: false, reason: "token_missing_config" };
|
|
174
185
|
}
|
|
175
186
|
if (!connectAuth?.token) {
|
|
187
|
+
limiter?.recordFailure(ip, rateLimitScope);
|
|
176
188
|
return { ok: false, reason: "token_missing" };
|
|
177
189
|
}
|
|
178
|
-
if (!
|
|
190
|
+
if (!safeEqualSecret(connectAuth.token, auth.token)) {
|
|
191
|
+
limiter?.recordFailure(ip, rateLimitScope);
|
|
179
192
|
return { ok: false, reason: "token_mismatch" };
|
|
180
193
|
}
|
|
194
|
+
limiter?.reset(ip, rateLimitScope);
|
|
181
195
|
return { ok: true, method: "token" };
|
|
182
196
|
}
|
|
183
197
|
if (auth.mode === "password") {
|
|
@@ -186,12 +200,16 @@ export async function authorizeGatewayConnect(params) {
|
|
|
186
200
|
return { ok: false, reason: "password_missing_config" };
|
|
187
201
|
}
|
|
188
202
|
if (!password) {
|
|
203
|
+
limiter?.recordFailure(ip, rateLimitScope);
|
|
189
204
|
return { ok: false, reason: "password_missing" };
|
|
190
205
|
}
|
|
191
|
-
if (!
|
|
206
|
+
if (!safeEqualSecret(password, auth.password)) {
|
|
207
|
+
limiter?.recordFailure(ip, rateLimitScope);
|
|
192
208
|
return { ok: false, reason: "password_mismatch" };
|
|
193
209
|
}
|
|
210
|
+
limiter?.reset(ip, rateLimitScope);
|
|
194
211
|
return { ok: true, method: "password" };
|
|
195
212
|
}
|
|
213
|
+
limiter?.recordFailure(ip, rateLimitScope);
|
|
196
214
|
return { ok: false, reason: "unauthorized" };
|
|
197
215
|
}
|
|
@@ -13,6 +13,7 @@ import { handleSkillsHttpRequest } from "./skills-http.js";
|
|
|
13
13
|
import { handleTaskEventsHttpRequest } from "./task-events-http.js";
|
|
14
14
|
import { handleToolsInvokeHttpRequest } from "./tools-invoke-http.js";
|
|
15
15
|
import { handleCronHttpRequest } from "./cron-http.js";
|
|
16
|
+
import { safeEqualSecret } from "../security/secret-equal.js";
|
|
16
17
|
function sendJson(res, status, body) {
|
|
17
18
|
res.statusCode = status;
|
|
18
19
|
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
@@ -30,16 +31,18 @@ export function createHooksRequestHandler(opts) {
|
|
|
30
31
|
return false;
|
|
31
32
|
}
|
|
32
33
|
const { token, fromQuery } = extractHookToken(req, url);
|
|
33
|
-
if (!token || token
|
|
34
|
+
if (!token || !safeEqualSecret(token, hooksConfig.token)) {
|
|
34
35
|
res.statusCode = 401;
|
|
35
36
|
res.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
36
37
|
res.end("Unauthorized");
|
|
37
38
|
return true;
|
|
38
39
|
}
|
|
39
40
|
if (fromQuery) {
|
|
40
|
-
|
|
41
|
-
"
|
|
42
|
-
|
|
41
|
+
sendJson(res, 400, {
|
|
42
|
+
error: "Hook token via query parameter is no longer supported. " +
|
|
43
|
+
"Use Authorization: Bearer <token> or X-Lemonade-Token header instead.",
|
|
44
|
+
});
|
|
45
|
+
return true;
|
|
43
46
|
}
|
|
44
47
|
if (req.method !== "POST") {
|
|
45
48
|
res.statusCode = 405;
|
|
@@ -46,6 +46,24 @@ export function archiveFileOnDisk(filePath, reason) {
|
|
|
46
46
|
fs.renameSync(filePath, archived);
|
|
47
47
|
return archived;
|
|
48
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Archives all transcript files for a given session.
|
|
51
|
+
* Best-effort: silently skips files that don't exist or fail to rename.
|
|
52
|
+
*/
|
|
53
|
+
export function archiveSessionTranscripts(opts) {
|
|
54
|
+
const archived = [];
|
|
55
|
+
for (const candidate of resolveSessionTranscriptCandidates(opts.sessionId, opts.storePath, opts.sessionFile, opts.agentId)) {
|
|
56
|
+
if (!fs.existsSync(candidate))
|
|
57
|
+
continue;
|
|
58
|
+
try {
|
|
59
|
+
archived.push(archiveFileOnDisk(candidate, opts.reason));
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// Best-effort.
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return archived;
|
|
66
|
+
}
|
|
49
67
|
function jsonUtf8Bytes(value) {
|
|
50
68
|
try {
|
|
51
69
|
return Buffer.byteLength(JSON.stringify(value), "utf8");
|
|
@@ -89,6 +89,22 @@ export async function handleSkillsHttpRequest(req, res, opts) {
|
|
|
89
89
|
return true;
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
|
+
if (url.pathname === "/api/skills/install-pack" || url.pathname === "/api/skills/install-pack/") {
|
|
93
|
+
if (req.method !== "POST") {
|
|
94
|
+
sendMethodNotAllowed(res, "POST");
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
try {
|
|
98
|
+
return await handleSkillsInstallPack(req, res);
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
console.error("[skills-http] Install-pack error:", err);
|
|
102
|
+
sendJson(res, 500, {
|
|
103
|
+
error: { message: "An internal error occurred", type: "internal_error" },
|
|
104
|
+
});
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
92
108
|
// Parse the skill name from the URL
|
|
93
109
|
const pathParts = url.pathname
|
|
94
110
|
.replace(/^\/api\/skills\/?/, "")
|
|
@@ -399,6 +415,55 @@ async function handleSkillsInstall(req, res) {
|
|
|
399
415
|
sendJson(res, result.ok ? 200 : 500, result);
|
|
400
416
|
return true;
|
|
401
417
|
}
|
|
418
|
+
async function handleSkillsInstallPack(req, res) {
|
|
419
|
+
const body = (await readJsonBodyOrError(req, res, 100_000));
|
|
420
|
+
if (!body)
|
|
421
|
+
return true;
|
|
422
|
+
const skillNames = Array.isArray(body.skillNames) ? body.skillNames.filter(Boolean) : [];
|
|
423
|
+
if (skillNames.length === 0) {
|
|
424
|
+
sendInvalidRequest(res, "skillNames array is required");
|
|
425
|
+
return true;
|
|
426
|
+
}
|
|
427
|
+
const cfg = loadConfig();
|
|
428
|
+
const workspaceDir = resolveAgentWorkspaceDir(cfg, resolveDefaultAgentId(cfg));
|
|
429
|
+
const report = buildWorkspaceSkillStatus(workspaceDir, {
|
|
430
|
+
config: cfg,
|
|
431
|
+
eligibility: { remote: getRemoteSkillEligibility() },
|
|
432
|
+
});
|
|
433
|
+
const toInstall = [];
|
|
434
|
+
for (const s of report.skills) {
|
|
435
|
+
if (!skillNames.includes(s.name))
|
|
436
|
+
continue;
|
|
437
|
+
if (!s.missing?.bins?.length)
|
|
438
|
+
continue;
|
|
439
|
+
const firstInstall = s.install?.[0];
|
|
440
|
+
if (firstInstall?.id) {
|
|
441
|
+
toInstall.push({ skillName: s.name, installId: firstInstall.id });
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
if (toInstall.length === 0) {
|
|
445
|
+
sendJson(res, 200, { ok: true, installed: [], message: "Nothing to install" });
|
|
446
|
+
return true;
|
|
447
|
+
}
|
|
448
|
+
// Respond immediately — installations happen in background
|
|
449
|
+
sendJson(res, 202, {
|
|
450
|
+
ok: true,
|
|
451
|
+
installing: toInstall.map((t) => t.skillName),
|
|
452
|
+
message: `Installing dependencies for ${toInstall.length} skill(s) in background`,
|
|
453
|
+
});
|
|
454
|
+
// Fire-and-forget background installs
|
|
455
|
+
for (const item of toInstall) {
|
|
456
|
+
installSkill({
|
|
457
|
+
workspaceDir,
|
|
458
|
+
skillName: item.skillName,
|
|
459
|
+
installId: item.installId,
|
|
460
|
+
config: cfg,
|
|
461
|
+
}).catch((err) => {
|
|
462
|
+
console.error(`[skills-http] Background install failed for ${item.skillName}:`, err);
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
return true;
|
|
466
|
+
}
|
|
402
467
|
async function handleDeleteSkill(res, skillName) {
|
|
403
468
|
const builtInNames = getBuiltInSkillNames();
|
|
404
469
|
if (builtInNames.has(skillName)) {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { createHash, timingSafeEqual } from "node:crypto";
|
|
2
|
+
/**
|
|
3
|
+
* Constant-time secret comparison that avoids leaking length information.
|
|
4
|
+
* Both values are SHA-256 hashed before comparison so `timingSafeEqual`
|
|
5
|
+
* always operates on equal-length buffers regardless of input lengths.
|
|
6
|
+
*/
|
|
7
|
+
export function safeEqualSecret(provided, expected) {
|
|
8
|
+
if (typeof provided !== "string" || typeof expected !== "string") {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
const hash = (s) => createHash("sha256").update(s).digest();
|
|
12
|
+
return timingSafeEqual(hash(provided), hash(expected));
|
|
13
|
+
}
|
|
@@ -16,7 +16,7 @@ import { resolveWhatsAppAccount } from "../accounts.js";
|
|
|
16
16
|
import { setActiveWebListener } from "../active-listener.js";
|
|
17
17
|
import { monitorWebInbox } from "../inbound.js";
|
|
18
18
|
import { computeBackoff, newConnectionId, resolveHeartbeatSeconds, resolveReconnectPolicy, sleepWithAbort, } from "../reconnect.js";
|
|
19
|
-
import { formatError, getStatusCode, getWebAuthAgeMs, readWebSelfId } from "../session.js";
|
|
19
|
+
import { formatError, getStatusCode, getWebAuthAgeMs, logoutWeb, readWebSelfId, } from "../session.js";
|
|
20
20
|
import { DEFAULT_WEB_MEDIA_BYTES } from "./constants.js";
|
|
21
21
|
import { whatsappHeartbeatLog, whatsappLog } from "./loggers.js";
|
|
22
22
|
import { buildMentionConfig } from "./mentions.js";
|
|
@@ -210,7 +210,15 @@ export async function monitorWebChannel(verbose, listenerFactory = monitorWebInb
|
|
|
210
210
|
reconnectAttempts,
|
|
211
211
|
maxAttempts: reconnectPolicy.maxAttempts,
|
|
212
212
|
}, "web reconnect: max attempts reached after connection failure");
|
|
213
|
-
|
|
213
|
+
// If 515 was seen, credentials are stale — clear them so the next
|
|
214
|
+
// gateway restart or manual re-link starts fresh.
|
|
215
|
+
if (has515Retried) {
|
|
216
|
+
await logoutWeb({ authDir: account.authDir, runtime });
|
|
217
|
+
runtime.error("WhatsApp session invalidated (repeated 515). Cleared credentials — re-link via QR to restore.");
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
runtime.error(`WhatsApp Web reconnect: max attempts reached (${reconnectAttempts}/${reconnectPolicy.maxAttempts}). Stopping web monitoring.`);
|
|
221
|
+
}
|
|
214
222
|
break;
|
|
215
223
|
}
|
|
216
224
|
const delay = computeBackoff(reconnectPolicy, reconnectAttempts);
|
package/dist/web/login-qr.js
CHANGED
|
@@ -210,6 +210,18 @@ export async function waitForWebLogin(opts = {}) {
|
|
|
210
210
|
if (restarted && isLoginFresh(login)) {
|
|
211
211
|
continue;
|
|
212
212
|
}
|
|
213
|
+
// 515 retry failed — credentials are stale. Clear them so the next
|
|
214
|
+
// pairing attempt starts completely fresh instead of reusing a
|
|
215
|
+
// broken session that WhatsApp's server has already invalidated.
|
|
216
|
+
await logoutWeb({
|
|
217
|
+
authDir: login.authDir,
|
|
218
|
+
isLegacyAuthDir: login.isLegacyAuthDir,
|
|
219
|
+
runtime,
|
|
220
|
+
});
|
|
221
|
+
const message = "WhatsApp pairing failed (code 515). Cleared session — please scan the QR code again.";
|
|
222
|
+
await resetActiveLogin(account.accountId, message);
|
|
223
|
+
runtime.log(danger(message));
|
|
224
|
+
return { connected: false, message };
|
|
213
225
|
}
|
|
214
226
|
const message = `WhatsApp login failed: ${login.error}`;
|
|
215
227
|
await resetActiveLogin(account.accountId, message);
|
|
@@ -10,9 +10,9 @@ case `uname` in
|
|
|
10
10
|
esac
|
|
11
11
|
|
|
12
12
|
if [ -z "$NODE_PATH" ]; then
|
|
13
|
-
export NODE_PATH="/Users/
|
|
13
|
+
export NODE_PATH="/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/acorn@8.15.0/node_modules/acorn/bin/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/acorn@8.15.0/node_modules/acorn/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/acorn@8.15.0/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/node_modules"
|
|
14
14
|
else
|
|
15
|
-
export NODE_PATH="/Users/
|
|
15
|
+
export NODE_PATH="/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/acorn@8.15.0/node_modules/acorn/bin/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/acorn@8.15.0/node_modules/acorn/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/acorn@8.15.0/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/node_modules:$NODE_PATH"
|
|
16
16
|
fi
|
|
17
17
|
if [ -x "$basedir/node" ]; then
|
|
18
18
|
exec "$basedir/node" "$basedir/../../../../node_modules/.pnpm/acorn@8.15.0/node_modules/acorn/bin/acorn" "$@"
|
|
@@ -10,9 +10,9 @@ case `uname` in
|
|
|
10
10
|
esac
|
|
11
11
|
|
|
12
12
|
if [ -z "$NODE_PATH" ]; then
|
|
13
|
-
export NODE_PATH="/Users/
|
|
13
|
+
export NODE_PATH="/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules:/Users/futureprooflabs/Desktop/clawdbot/node_modules:/Users/futureprooflabs/Desktop/node_modules:/Users/futureprooflabs/node_modules:/Users/node_modules:/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/node_modules"
|
|
14
14
|
else
|
|
15
|
-
export NODE_PATH="/Users/
|
|
15
|
+
export NODE_PATH="/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules:/Users/futureprooflabs/Desktop/clawdbot/node_modules:/Users/futureprooflabs/Desktop/node_modules:/Users/futureprooflabs/node_modules:/Users/node_modules:/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/node_modules:$NODE_PATH"
|
|
16
16
|
fi
|
|
17
17
|
if [ -x "$basedir/node" ]; then
|
|
18
18
|
exec "$basedir/node" "$basedir/../lemonade/lemonade.mjs" "$@"
|
|
@@ -10,9 +10,9 @@ case `uname` in
|
|
|
10
10
|
esac
|
|
11
11
|
|
|
12
12
|
if [ -z "$NODE_PATH" ]; then
|
|
13
|
-
export NODE_PATH="/Users/
|
|
13
|
+
export NODE_PATH="/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules:/Users/futureprooflabs/Desktop/clawdbot/node_modules:/Users/futureprooflabs/Desktop/node_modules:/Users/futureprooflabs/node_modules:/Users/node_modules:/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/node_modules"
|
|
14
14
|
else
|
|
15
|
-
export NODE_PATH="/Users/
|
|
15
|
+
export NODE_PATH="/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules:/Users/futureprooflabs/Desktop/clawdbot/node_modules:/Users/futureprooflabs/Desktop/node_modules:/Users/futureprooflabs/node_modules:/Users/node_modules:/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/node_modules:$NODE_PATH"
|
|
16
16
|
fi
|
|
17
17
|
if [ -x "$basedir/node" ]; then
|
|
18
18
|
exec "$basedir/node" "$basedir/../lemonade/lemonade.mjs" "$@"
|
|
@@ -10,9 +10,9 @@ case `uname` in
|
|
|
10
10
|
esac
|
|
11
11
|
|
|
12
12
|
if [ -z "$NODE_PATH" ]; then
|
|
13
|
-
export NODE_PATH="/Users/
|
|
13
|
+
export NODE_PATH="/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules:/Users/futureprooflabs/Desktop/clawdbot/node_modules:/Users/futureprooflabs/Desktop/node_modules:/Users/futureprooflabs/node_modules:/Users/node_modules:/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/node_modules"
|
|
14
14
|
else
|
|
15
|
-
export NODE_PATH="/Users/
|
|
15
|
+
export NODE_PATH="/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules:/Users/futureprooflabs/Desktop/clawdbot/node_modules:/Users/futureprooflabs/Desktop/node_modules:/Users/futureprooflabs/node_modules:/Users/node_modules:/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/node_modules:$NODE_PATH"
|
|
16
16
|
fi
|
|
17
17
|
if [ -x "$basedir/node" ]; then
|
|
18
18
|
exec "$basedir/node" "$basedir/../lemonade/lemonade.mjs" "$@"
|
|
@@ -10,9 +10,9 @@ case `uname` in
|
|
|
10
10
|
esac
|
|
11
11
|
|
|
12
12
|
if [ -z "$NODE_PATH" ]; then
|
|
13
|
-
export NODE_PATH="/Users/
|
|
13
|
+
export NODE_PATH="/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/markdown-it@14.1.0/node_modules/markdown-it/bin/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/markdown-it@14.1.0/node_modules/markdown-it/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/markdown-it@14.1.0/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/node_modules"
|
|
14
14
|
else
|
|
15
|
-
export NODE_PATH="/Users/
|
|
15
|
+
export NODE_PATH="/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/markdown-it@14.1.0/node_modules/markdown-it/bin/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/markdown-it@14.1.0/node_modules/markdown-it/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/markdown-it@14.1.0/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/node_modules:$NODE_PATH"
|
|
16
16
|
fi
|
|
17
17
|
if [ -x "$basedir/node" ]; then
|
|
18
18
|
exec "$basedir/node" "$basedir/../markdown-it/bin/markdown-it.mjs" "$@"
|
|
@@ -10,9 +10,9 @@ case `uname` in
|
|
|
10
10
|
esac
|
|
11
11
|
|
|
12
12
|
if [ -z "$NODE_PATH" ]; then
|
|
13
|
-
export NODE_PATH="/Users/
|
|
13
|
+
export NODE_PATH="/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules:/Users/futureprooflabs/Desktop/clawdbot/node_modules:/Users/futureprooflabs/Desktop/node_modules:/Users/futureprooflabs/node_modules:/Users/node_modules:/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/node_modules"
|
|
14
14
|
else
|
|
15
|
-
export NODE_PATH="/Users/
|
|
15
|
+
export NODE_PATH="/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules:/Users/futureprooflabs/Desktop/clawdbot/node_modules:/Users/futureprooflabs/Desktop/node_modules:/Users/futureprooflabs/node_modules:/Users/node_modules:/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/node_modules:$NODE_PATH"
|
|
16
16
|
fi
|
|
17
17
|
if [ -x "$basedir/node" ]; then
|
|
18
18
|
exec "$basedir/node" "$basedir/../lemonade/lemonade.mjs" "$@"
|
|
@@ -10,9 +10,9 @@ case `uname` in
|
|
|
10
10
|
esac
|
|
11
11
|
|
|
12
12
|
if [ -z "$NODE_PATH" ]; then
|
|
13
|
-
export NODE_PATH="/Users/
|
|
13
|
+
export NODE_PATH="/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/apache-arrow@18.1.0/node_modules/apache-arrow/bin/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/apache-arrow@18.1.0/node_modules/apache-arrow/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/apache-arrow@18.1.0/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/node_modules"
|
|
14
14
|
else
|
|
15
|
-
export NODE_PATH="/Users/
|
|
15
|
+
export NODE_PATH="/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/apache-arrow@18.1.0/node_modules/apache-arrow/bin/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/apache-arrow@18.1.0/node_modules/apache-arrow/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/apache-arrow@18.1.0/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/node_modules:$NODE_PATH"
|
|
16
16
|
fi
|
|
17
17
|
if [ -x "$basedir/node" ]; then
|
|
18
18
|
exec "$basedir/node" "$basedir/../../../../node_modules/.pnpm/apache-arrow@18.1.0/node_modules/apache-arrow/bin/arrow2csv.js" "$@"
|
|
@@ -10,9 +10,9 @@ case `uname` in
|
|
|
10
10
|
esac
|
|
11
11
|
|
|
12
12
|
if [ -z "$NODE_PATH" ]; then
|
|
13
|
-
export NODE_PATH="/Users/
|
|
13
|
+
export NODE_PATH="/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/openai@6.17.0_ws@8.19.0_zod@4.3.6/node_modules/openai/bin/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/openai@6.17.0_ws@8.19.0_zod@4.3.6/node_modules/openai/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/openai@6.17.0_ws@8.19.0_zod@4.3.6/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/node_modules"
|
|
14
14
|
else
|
|
15
|
-
export NODE_PATH="/Users/
|
|
15
|
+
export NODE_PATH="/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/openai@6.17.0_ws@8.19.0_zod@4.3.6/node_modules/openai/bin/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/openai@6.17.0_ws@8.19.0_zod@4.3.6/node_modules/openai/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/openai@6.17.0_ws@8.19.0_zod@4.3.6/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/node_modules:$NODE_PATH"
|
|
16
16
|
fi
|
|
17
17
|
if [ -x "$basedir/node" ]; then
|
|
18
18
|
exec "$basedir/node" "$basedir/../openai/bin/cli" "$@"
|
|
@@ -10,9 +10,9 @@ case `uname` in
|
|
|
10
10
|
esac
|
|
11
11
|
|
|
12
12
|
if [ -z "$NODE_PATH" ]; then
|
|
13
|
-
export NODE_PATH="/Users/
|
|
13
|
+
export NODE_PATH="/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules:/Users/futureprooflabs/Desktop/clawdbot/node_modules:/Users/futureprooflabs/Desktop/node_modules:/Users/futureprooflabs/node_modules:/Users/node_modules:/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/node_modules"
|
|
14
14
|
else
|
|
15
|
-
export NODE_PATH="/Users/
|
|
15
|
+
export NODE_PATH="/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules:/Users/futureprooflabs/Desktop/clawdbot/node_modules:/Users/futureprooflabs/Desktop/node_modules:/Users/futureprooflabs/node_modules:/Users/node_modules:/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/node_modules:$NODE_PATH"
|
|
16
16
|
fi
|
|
17
17
|
if [ -x "$basedir/node" ]; then
|
|
18
18
|
exec "$basedir/node" "$basedir/../lemonade/lemonade.mjs" "$@"
|
|
@@ -10,9 +10,9 @@ case `uname` in
|
|
|
10
10
|
esac
|
|
11
11
|
|
|
12
12
|
if [ -z "$NODE_PATH" ]; then
|
|
13
|
-
export NODE_PATH="/Users/
|
|
13
|
+
export NODE_PATH="/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules:/Users/futureprooflabs/Desktop/clawdbot/node_modules:/Users/futureprooflabs/Desktop/node_modules:/Users/futureprooflabs/node_modules:/Users/node_modules:/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/node_modules"
|
|
14
14
|
else
|
|
15
|
-
export NODE_PATH="/Users/
|
|
15
|
+
export NODE_PATH="/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules:/Users/futureprooflabs/Desktop/clawdbot/node_modules:/Users/futureprooflabs/Desktop/node_modules:/Users/futureprooflabs/node_modules:/Users/node_modules:/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/node_modules:$NODE_PATH"
|
|
16
16
|
fi
|
|
17
17
|
if [ -x "$basedir/node" ]; then
|
|
18
18
|
exec "$basedir/node" "$basedir/../lemonade/lemonade.mjs" "$@"
|
|
@@ -10,9 +10,9 @@ case `uname` in
|
|
|
10
10
|
esac
|
|
11
11
|
|
|
12
12
|
if [ -z "$NODE_PATH" ]; then
|
|
13
|
-
export NODE_PATH="/Users/
|
|
13
|
+
export NODE_PATH="/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/bin/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/typescript@5.9.3/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/node_modules"
|
|
14
14
|
else
|
|
15
|
-
export NODE_PATH="/Users/
|
|
15
|
+
export NODE_PATH="/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/bin/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/typescript@5.9.3/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/node_modules:$NODE_PATH"
|
|
16
16
|
fi
|
|
17
17
|
if [ -x "$basedir/node" ]; then
|
|
18
18
|
exec "$basedir/node" "$basedir/../../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/bin/tsc" "$@"
|
|
@@ -10,9 +10,9 @@ case `uname` in
|
|
|
10
10
|
esac
|
|
11
11
|
|
|
12
12
|
if [ -z "$NODE_PATH" ]; then
|
|
13
|
-
export NODE_PATH="/Users/
|
|
13
|
+
export NODE_PATH="/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/bin/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/typescript@5.9.3/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/node_modules"
|
|
14
14
|
else
|
|
15
|
-
export NODE_PATH="/Users/
|
|
15
|
+
export NODE_PATH="/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/bin/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/typescript@5.9.3/node_modules:/Users/futureprooflabs/Desktop/clawdbot/lemonade/node_modules/.pnpm/node_modules:$NODE_PATH"
|
|
16
16
|
fi
|
|
17
17
|
if [ -x "$basedir/node" ]; then
|
|
18
18
|
exec "$basedir/node" "$basedir/../../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/bin/tsserver" "$@"
|