seclaw 0.1.11 → 0.1.12
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/cli.js +34 -6
- package/dist/runtime/agent.js +67 -29
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -73,7 +73,20 @@ async function fetchTunnelUrlFromLogs(cwd) {
|
|
|
73
73
|
}
|
|
74
74
|
async function setTelegramWebhook(botToken, tunnelUrl) {
|
|
75
75
|
const webhookUrl = `${tunnelUrl}/webhook`;
|
|
76
|
-
|
|
76
|
+
const hostname = tunnelUrl.replace("https://", "");
|
|
77
|
+
for (let i = 0; i < 30; i++) {
|
|
78
|
+
try {
|
|
79
|
+
const { resolve4: resolve42 } = await import("dns/promises");
|
|
80
|
+
await resolve42(hostname);
|
|
81
|
+
break;
|
|
82
|
+
} catch {
|
|
83
|
+
if (i === 29) {
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
await new Promise((r) => setTimeout(r, 2e3));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
for (let attempt = 0; attempt < 5; attempt++) {
|
|
77
90
|
try {
|
|
78
91
|
const res = await fetch(
|
|
79
92
|
`https://api.telegram.org/bot${botToken}/setWebhook`,
|
|
@@ -89,9 +102,13 @@ async function setTelegramWebhook(botToken, tunnelUrl) {
|
|
|
89
102
|
);
|
|
90
103
|
const data = await res.json();
|
|
91
104
|
if (data.ok) return true;
|
|
105
|
+
if (data.description?.includes("resolve host")) {
|
|
106
|
+
await new Promise((r) => setTimeout(r, 5e3));
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
92
109
|
} catch {
|
|
93
110
|
}
|
|
94
|
-
if (attempt <
|
|
111
|
+
if (attempt < 4) await new Promise((r) => setTimeout(r, 3e3));
|
|
95
112
|
}
|
|
96
113
|
try {
|
|
97
114
|
const res = await fetch(
|
|
@@ -542,10 +559,20 @@ ENTRYPOINT ["/bin/sh"]
|
|
|
542
559
|
const script = `#!/bin/sh
|
|
543
560
|
# Auto-updates Telegram webhook when tunnel URL changes on restart.
|
|
544
561
|
|
|
562
|
+
wait_for_dns() {
|
|
563
|
+
HOST=$(echo "$1" | sed 's|https://||')
|
|
564
|
+
for i in $(seq 1 30); do
|
|
565
|
+
nslookup "$HOST" >/dev/null 2>&1 && return 0
|
|
566
|
+
printf '[tunnel] Waiting for DNS (%s, attempt %d/30)...\\n' "$HOST" "$i" >&2
|
|
567
|
+
sleep 2
|
|
568
|
+
done
|
|
569
|
+
return 1
|
|
570
|
+
}
|
|
571
|
+
|
|
545
572
|
set_webhook() {
|
|
546
573
|
URL="$1"
|
|
547
|
-
for
|
|
548
|
-
|
|
574
|
+
wait_for_dns "$URL" || { printf '[tunnel] DNS resolution failed for %s\\n' "$URL" >&2; return 1; }
|
|
575
|
+
for i in 1 2 3 4 5; do
|
|
549
576
|
RESP=$(curl -s -X POST "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/setWebhook" \\
|
|
550
577
|
-H "Content-Type: application/json" \\
|
|
551
578
|
-d "{\\"url\\":\\"$URL/webhook\\",\\"allowed_updates\\":[\\"message\\",\\"callback_query\\"]}")
|
|
@@ -556,6 +583,7 @@ set_webhook() {
|
|
|
556
583
|
;;
|
|
557
584
|
esac
|
|
558
585
|
printf '[tunnel] Webhook attempt %d failed: %s\\n' "$i" "$RESP" >&2
|
|
586
|
+
sleep 5
|
|
559
587
|
done
|
|
560
588
|
return 1
|
|
561
589
|
}
|
|
@@ -2428,10 +2456,10 @@ async function checkTelegram(projectDir, tunnelCheck) {
|
|
|
2428
2456
|
const wh = whData.result;
|
|
2429
2457
|
const tunnelUrl = tunnelCheck.tunnelUrl || "";
|
|
2430
2458
|
const webhookFix = async () => {
|
|
2431
|
-
const freshUrl = await getTunnelUrl(projectDir,
|
|
2459
|
+
const freshUrl = await getTunnelUrl(projectDir, 15);
|
|
2432
2460
|
if (!freshUrl) return "No tunnel URL available \u2014 fix tunnel first";
|
|
2433
2461
|
const ok = await setTelegramWebhook(botToken, freshUrl);
|
|
2434
|
-
return ok ? `Webhook set to ${freshUrl}` : "Could not set webhook";
|
|
2462
|
+
return ok ? `Webhook set to ${freshUrl}` : "Could not set webhook \u2014 DNS may still be propagating, try again in 30s";
|
|
2435
2463
|
};
|
|
2436
2464
|
if (!wh.url) {
|
|
2437
2465
|
return {
|
package/dist/runtime/agent.js
CHANGED
|
@@ -1115,12 +1115,7 @@ var AUTO_BASE_PROMPT = `You are a personal AI assistant running on seclaw. You h
|
|
|
1115
1115
|
## Communication
|
|
1116
1116
|
- Detect the user's language and respond in the same language
|
|
1117
1117
|
- Keep Telegram messages concise \u2014 use bullet points and short paragraphs
|
|
1118
|
-
- Be proactive when you have relevant context to share
|
|
1119
|
-
|
|
1120
|
-
## Response Format
|
|
1121
|
-
At the end of every response, write on a new line:
|
|
1122
|
-
--- CapabilityName
|
|
1123
|
-
Where CapabilityName is the capability you primarily used (e.g. "Inbox Management", "Research & Intelligence"). If no specific capability applies, write: --- General`;
|
|
1118
|
+
- Be proactive when you have relevant context to share`;
|
|
1124
1119
|
function focusBasePrompt(capId) {
|
|
1125
1120
|
const displayName = capId.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
1126
1121
|
return `You are a specialized AI agent running on seclaw, focused exclusively on: ${displayName}.
|
|
@@ -1130,11 +1125,7 @@ IMPORTANT: You are in FOCUS MODE. Only use the capability described below. Do NO
|
|
|
1130
1125
|
## Communication
|
|
1131
1126
|
- Detect the user's language and respond in the same language
|
|
1132
1127
|
- Keep Telegram messages concise \u2014 use bullet points and short paragraphs
|
|
1133
|
-
- Be proactive when you have relevant context to share
|
|
1134
|
-
|
|
1135
|
-
## Response Format
|
|
1136
|
-
At the end of every response, write on a new line:
|
|
1137
|
-
--- ${displayName}`;
|
|
1128
|
+
- Be proactive when you have relevant context to share`;
|
|
1138
1129
|
}
|
|
1139
1130
|
function loadConfig() {
|
|
1140
1131
|
return {
|
|
@@ -1228,7 +1219,18 @@ function composeCapabilityPrompt(capabilities, mode) {
|
|
|
1228
1219
|
console.warn("[config] No capability prompts loaded, using base prompt only");
|
|
1229
1220
|
return base;
|
|
1230
1221
|
}
|
|
1231
|
-
|
|
1222
|
+
const capNames = capabilities.map(
|
|
1223
|
+
(id) => id.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ")
|
|
1224
|
+
);
|
|
1225
|
+
const footerInstruction = mode === "auto" ? `## MANDATORY Response Footer
|
|
1226
|
+
You MUST end EVERY response with this exact format on a new line:
|
|
1227
|
+
--- CapabilityName
|
|
1228
|
+
Replace CapabilityName with the capability you used (one of: ${capNames.join(", ")}). If none applies, write: --- General
|
|
1229
|
+
This line is REQUIRED. Never skip it.` : `## MANDATORY Response Footer
|
|
1230
|
+
You MUST end EVERY response with this exact format on a new line:
|
|
1231
|
+
--- ${capNames[0]}
|
|
1232
|
+
This line is REQUIRED. Never skip it.`;
|
|
1233
|
+
return base + "\n\n" + sections.join("\n\n") + "\n\n" + footerInstruction;
|
|
1232
1234
|
}
|
|
1233
1235
|
function loadInstalledCapabilities(workspace) {
|
|
1234
1236
|
const installedPath = resolve2(workspace, "config", "installed.json");
|
|
@@ -1477,12 +1479,12 @@ ${userText}`;
|
|
|
1477
1479
|
let response = await runAgent(config2, toolCtx2, history, augmented);
|
|
1478
1480
|
console.log(`[telegram] Reply (${response.length} chars): ${response.substring(0, 120)}`);
|
|
1479
1481
|
let footer = "";
|
|
1480
|
-
const footerMatch = response.match(/\n
|
|
1482
|
+
const footerMatch = response.match(/\n[-—]{2,3}\s*(.+?)\s*$/);
|
|
1481
1483
|
if (footerMatch) {
|
|
1482
|
-
response = response.replace(/\n
|
|
1484
|
+
response = response.replace(/\n[-—]{2,3}\s*.+?\s*$/, "").trimEnd();
|
|
1483
1485
|
footer = `
|
|
1484
1486
|
|
|
1485
|
-
|
|
1487
|
+
\u2014 ${footerMatch[1].trim()}`;
|
|
1486
1488
|
}
|
|
1487
1489
|
saveHistory(chatId, config2.workspace, [
|
|
1488
1490
|
...history,
|
|
@@ -2024,14 +2026,43 @@ async function editMessageWithButtons(token, chatId, messageId, text, keyboard)
|
|
|
2024
2026
|
} catch {
|
|
2025
2027
|
}
|
|
2026
2028
|
}
|
|
2029
|
+
var TEMPLATE_CATALOG = [
|
|
2030
|
+
{ id: "productivity-agent", name: "Productivity Agent", price: "Free" },
|
|
2031
|
+
{ id: "data-analyst", name: "Data Analyst", price: "Free" },
|
|
2032
|
+
{ id: "inbox-agent", name: "Inbox Agent", price: "$19" },
|
|
2033
|
+
{ id: "reddit-hn-digest", name: "Reddit & HN Digest", price: "$19" },
|
|
2034
|
+
{ id: "youtube-digest", name: "YouTube Digest", price: "$19" },
|
|
2035
|
+
{ id: "health-tracker", name: "Health Tracker", price: "$29" },
|
|
2036
|
+
{ id: "earnings-tracker", name: "Earnings Tracker", price: "$29" },
|
|
2037
|
+
{ id: "research-agent", name: "Research Agent", price: "$39" },
|
|
2038
|
+
{ id: "knowledge-base", name: "Knowledge Base", price: "$39" },
|
|
2039
|
+
{ id: "family-calendar", name: "Family Calendar", price: "$39" },
|
|
2040
|
+
{ id: "content-agent", name: "Content Agent", price: "$49" },
|
|
2041
|
+
{ id: "personal-crm", name: "Personal CRM", price: "$49" },
|
|
2042
|
+
{ id: "youtube-creator", name: "YouTube Creator", price: "$69" },
|
|
2043
|
+
{ id: "devops-agent", name: "DevOps Agent", price: "$79" },
|
|
2044
|
+
{ id: "customer-service", name: "Customer Service", price: "$79" },
|
|
2045
|
+
{ id: "sales-agent", name: "Sales Agent", price: "$79" },
|
|
2046
|
+
{ id: "six-agent-company", name: "6-Agent Company", price: "$149" }
|
|
2047
|
+
];
|
|
2027
2048
|
async function handleTemplates(chatId, config2) {
|
|
2028
2049
|
const capabilities = loadInstalledCapabilities(config2.workspace);
|
|
2029
2050
|
if (capabilities.length === 0) {
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
)
|
|
2051
|
+
const lines2 = TEMPLATE_CATALOG.map((t) => {
|
|
2052
|
+
const tag = t.price === "Free" ? "FREE" : t.price;
|
|
2053
|
+
return `\u2022 *${t.name}* \u2014 ${tag}`;
|
|
2054
|
+
});
|
|
2055
|
+
const text2 = `*seclaw Templates (${TEMPLATE_CATALOG.length})*
|
|
2056
|
+
|
|
2057
|
+
` + lines2.join("\n") + "\n\n_Tap a template to view details:_";
|
|
2058
|
+
const keyboard2 = [];
|
|
2059
|
+
for (const t of TEMPLATE_CATALOG) {
|
|
2060
|
+
keyboard2.push([{
|
|
2061
|
+
text: `${t.name} \u2014 ${t.price}`,
|
|
2062
|
+
url: `https://seclawai.com/templates/${t.id}`
|
|
2063
|
+
}]);
|
|
2064
|
+
}
|
|
2065
|
+
await sendMessageWithButtons(config2.telegramToken, chatId, text2, keyboard2);
|
|
2035
2066
|
return;
|
|
2036
2067
|
}
|
|
2037
2068
|
const activeMode = getActiveMode(config2.workspace);
|
|
@@ -2062,13 +2093,20 @@ async function handleTemplates(chatId, config2) {
|
|
|
2062
2093
|
const activeName = activeMode.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
2063
2094
|
modeLabel = `${activeName} (focus mode)`;
|
|
2064
2095
|
}
|
|
2065
|
-
const
|
|
2096
|
+
const notInstalled = TEMPLATE_CATALOG.filter((t) => !capabilities.includes(t.id));
|
|
2097
|
+
let text = `*Installed Templates (${capabilities.length})*
|
|
2066
2098
|
|
|
2067
2099
|
${lines.join("\n")}
|
|
2068
2100
|
|
|
2069
2101
|
Mode: *${modeLabel}*`;
|
|
2102
|
+
if (notInstalled.length > 0) {
|
|
2103
|
+
text += `
|
|
2104
|
+
|
|
2105
|
+
*Browse More (${notInstalled.length})*
|
|
2106
|
+
_Tap to view details:_`;
|
|
2107
|
+
}
|
|
2108
|
+
const keyboard = [];
|
|
2070
2109
|
if (capabilities.length >= 2) {
|
|
2071
|
-
const keyboard = [];
|
|
2072
2110
|
const autoLabel = activeMode === "auto" ? "\u2705 Auto (All Templates)" : "\u{1F504} Auto (All Templates)";
|
|
2073
2111
|
keyboard.push([{ text: autoLabel, callback_data: "cap_switch:auto" }]);
|
|
2074
2112
|
const capButtons = [];
|
|
@@ -2080,14 +2118,14 @@ Mode: *${modeLabel}*`;
|
|
|
2080
2118
|
for (let i = 0; i < capButtons.length; i += 2) {
|
|
2081
2119
|
keyboard.push(capButtons.slice(i, i + 2));
|
|
2082
2120
|
}
|
|
2083
|
-
await sendMessageWithButtons(config2.telegramToken, chatId, text, keyboard);
|
|
2084
|
-
} else {
|
|
2085
|
-
await sendMessage(
|
|
2086
|
-
config2.telegramToken,
|
|
2087
|
-
chatId,
|
|
2088
|
-
text + "\n\nAdd more templates to switch modes:\n`npx seclaw add <template> --key YOUR_KEY`"
|
|
2089
|
-
);
|
|
2090
2121
|
}
|
|
2122
|
+
for (const t of notInstalled) {
|
|
2123
|
+
keyboard.push([{
|
|
2124
|
+
text: `${t.name} \u2014 ${t.price}`,
|
|
2125
|
+
url: `https://seclawai.com/templates/${t.id}`
|
|
2126
|
+
}]);
|
|
2127
|
+
}
|
|
2128
|
+
await sendMessageWithButtons(config2.telegramToken, chatId, text, keyboard);
|
|
2091
2129
|
}
|
|
2092
2130
|
async function handleSwitchCallback(chatId, msgId, capId, config2) {
|
|
2093
2131
|
const capabilities = loadInstalledCapabilities(config2.workspace);
|