@harbinger-ai/harbinger 0.1.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/LICENSE +21 -0
- package/README.md +406 -0
- package/agents/README.md +76 -0
- package/agents/_template/CONFIG.yaml +7 -0
- package/agents/_template/HEARTBEAT.md +59 -0
- package/agents/_template/IDENTITY.md +4 -0
- package/agents/_template/SKILLS.md +1 -0
- package/agents/_template/SOUL.md +25 -0
- package/agents/_template/TOOLS.md +3 -0
- package/agents/binary-reverser/CONFIG.yaml +21 -0
- package/agents/binary-reverser/HEARTBEAT.md +65 -0
- package/agents/binary-reverser/IDENTITY.md +1 -0
- package/agents/binary-reverser/SKILLS.md +1 -0
- package/agents/binary-reverser/SOUL.md +23 -0
- package/agents/binary-reverser/TOOLS.md +99 -0
- package/agents/browser-agent/CONFIG.yaml +20 -0
- package/agents/browser-agent/HEARTBEAT.md +79 -0
- package/agents/browser-agent/IDENTITY.md +5 -0
- package/agents/browser-agent/SKILLS.md +86 -0
- package/agents/browser-agent/SOUL.md +23 -0
- package/agents/browser-agent/TOOLS.md +186 -0
- package/agents/cloud-infiltrator/CONFIG.yaml +22 -0
- package/agents/cloud-infiltrator/HEARTBEAT.md +78 -0
- package/agents/cloud-infiltrator/IDENTITY.md +1 -0
- package/agents/cloud-infiltrator/SKILLS.md +1 -0
- package/agents/cloud-infiltrator/SOUL.md +23 -0
- package/agents/cloud-infiltrator/TOOLS.md +68 -0
- package/agents/coding-assistant/CONFIG.yaml +22 -0
- package/agents/coding-assistant/HEARTBEAT.md +57 -0
- package/agents/coding-assistant/IDENTITY.md +5 -0
- package/agents/coding-assistant/SKILLS.md +69 -0
- package/agents/coding-assistant/SOUL.md +60 -0
- package/agents/coding-assistant/TOOLS.md +168 -0
- package/agents/learning-agent/CONFIG.yaml +21 -0
- package/agents/learning-agent/HEARTBEAT.md +63 -0
- package/agents/learning-agent/IDENTITY.md +5 -0
- package/agents/learning-agent/SKILLS.md +86 -0
- package/agents/learning-agent/SOUL.md +77 -0
- package/agents/learning-agent/TOOLS.md +145 -0
- package/agents/maintainer/CONFIG.yaml +31 -0
- package/agents/maintainer/HEARTBEAT.md +28 -0
- package/agents/maintainer/IDENTITY.md +33 -0
- package/agents/maintainer/SKILLS.md +24 -0
- package/agents/maintainer/SOUL.md +61 -0
- package/agents/maintainer/TOOLS.md +29 -0
- package/agents/maintainer/lib/engine.js +279 -0
- package/agents/maintainer/lib/safe-fixer.js +183 -0
- package/agents/morning-brief/CONFIG.yaml +22 -0
- package/agents/morning-brief/HEARTBEAT.md +60 -0
- package/agents/morning-brief/IDENTITY.md +5 -0
- package/agents/morning-brief/SKILLS.md +56 -0
- package/agents/morning-brief/SOUL.md +64 -0
- package/agents/morning-brief/TOOLS.md +112 -0
- package/agents/osint-detective/CONFIG.yaml +24 -0
- package/agents/osint-detective/HEARTBEAT.md +66 -0
- package/agents/osint-detective/IDENTITY.md +1 -0
- package/agents/osint-detective/SKILLS.md +1 -0
- package/agents/osint-detective/SOUL.md +23 -0
- package/agents/osint-detective/TOOLS.md +81 -0
- package/agents/recon-scout/CONFIG.yaml +22 -0
- package/agents/recon-scout/HEARTBEAT.md +79 -0
- package/agents/recon-scout/IDENTITY.md +1 -0
- package/agents/recon-scout/SKILLS.md +1 -0
- package/agents/recon-scout/SOUL.md +23 -0
- package/agents/recon-scout/TOOLS.md +93 -0
- package/agents/report-writer/CONFIG.yaml +21 -0
- package/agents/report-writer/HEARTBEAT.md +63 -0
- package/agents/report-writer/IDENTITY.md +1 -0
- package/agents/report-writer/SKILLS.md +1 -0
- package/agents/report-writer/SOUL.md +23 -0
- package/agents/report-writer/TOOLS.md +69 -0
- package/agents/shared/README.md +13 -0
- package/agents/web-hacker/CONFIG.yaml +24 -0
- package/agents/web-hacker/HEARTBEAT.md +78 -0
- package/agents/web-hacker/IDENTITY.md +1 -0
- package/agents/web-hacker/SKILLS.md +1 -0
- package/agents/web-hacker/SOUL.md +23 -0
- package/agents/web-hacker/TOOLS.md +86 -0
- package/api/CLAUDE.md +19 -0
- package/api/index.js +274 -0
- package/bin/cli.js +620 -0
- package/bin/local.sh +31 -0
- package/bin/postinstall.js +63 -0
- package/config/index.js +24 -0
- package/config/instrumentation.js +93 -0
- package/drizzle/0000_initial.sql +52 -0
- package/drizzle/0001_bounty_and_registry.sql +82 -0
- package/drizzle/0002_sync_columns.sql +7 -0
- package/drizzle/0003_graceful_bloodscream.sql +86 -0
- package/drizzle/meta/0000_snapshot.json +321 -0
- package/drizzle/meta/0003_snapshot.json +878 -0
- package/drizzle/meta/_journal.json +34 -0
- package/drizzle/relations.ts +3 -0
- package/drizzle/schema.ts +145 -0
- package/lib/actions.js +47 -0
- package/lib/agents.js +166 -0
- package/lib/ai/agent.js +96 -0
- package/lib/ai/autonomous-engine.js +261 -0
- package/lib/ai/index.js +359 -0
- package/lib/ai/model-router.js +254 -0
- package/lib/ai/model.js +73 -0
- package/lib/ai/tools.js +84 -0
- package/lib/auth/actions.js +28 -0
- package/lib/auth/config.js +27 -0
- package/lib/auth/edge-config.js +27 -0
- package/lib/auth/index.js +27 -0
- package/lib/auth/middleware.js +53 -0
- package/lib/bounty/actions.js +119 -0
- package/lib/bounty/findings.js +64 -0
- package/lib/bounty/programs.js +34 -0
- package/lib/bounty/sync-targets.js +267 -0
- package/lib/bounty/targets.js +33 -0
- package/lib/channels/base.js +56 -0
- package/lib/channels/index.js +15 -0
- package/lib/channels/telegram.js +148 -0
- package/lib/chat/actions.js +288 -0
- package/lib/chat/api.js +135 -0
- package/lib/chat/components/app-sidebar.js +237 -0
- package/lib/chat/components/app-sidebar.jsx +289 -0
- package/lib/chat/components/chat-header.js +27 -0
- package/lib/chat/components/chat-header.jsx +37 -0
- package/lib/chat/components/chat-input.js +230 -0
- package/lib/chat/components/chat-input.jsx +228 -0
- package/lib/chat/components/chat-nav-context.js +11 -0
- package/lib/chat/components/chat-nav-context.jsx +11 -0
- package/lib/chat/components/chat-page.js +81 -0
- package/lib/chat/components/chat-page.jsx +100 -0
- package/lib/chat/components/chat.js +150 -0
- package/lib/chat/components/chat.jsx +182 -0
- package/lib/chat/components/chats-page.js +302 -0
- package/lib/chat/components/chats-page.jsx +330 -0
- package/lib/chat/components/crons-page.js +172 -0
- package/lib/chat/components/crons-page.jsx +244 -0
- package/lib/chat/components/enhanced-tool-call.js +103 -0
- package/lib/chat/components/enhanced-tool-call.jsx +139 -0
- package/lib/chat/components/findings-page.js +175 -0
- package/lib/chat/components/findings-page.jsx +214 -0
- package/lib/chat/components/greeting.js +22 -0
- package/lib/chat/components/greeting.jsx +26 -0
- package/lib/chat/components/icons.js +777 -0
- package/lib/chat/components/icons.jsx +741 -0
- package/lib/chat/components/index.js +26 -0
- package/lib/chat/components/mcp-page.js +260 -0
- package/lib/chat/components/mcp-page.jsx +355 -0
- package/lib/chat/components/message.js +289 -0
- package/lib/chat/components/message.jsx +315 -0
- package/lib/chat/components/messages.js +66 -0
- package/lib/chat/components/messages.jsx +77 -0
- package/lib/chat/components/notifications-page.js +56 -0
- package/lib/chat/components/notifications-page.jsx +87 -0
- package/lib/chat/components/page-layout.js +21 -0
- package/lib/chat/components/page-layout.jsx +28 -0
- package/lib/chat/components/registry-page.js +222 -0
- package/lib/chat/components/registry-page.jsx +255 -0
- package/lib/chat/components/settings-layout.js +40 -0
- package/lib/chat/components/settings-layout.jsx +54 -0
- package/lib/chat/components/settings-secrets-page.js +216 -0
- package/lib/chat/components/settings-secrets-page.jsx +264 -0
- package/lib/chat/components/sidebar-history-item.js +132 -0
- package/lib/chat/components/sidebar-history-item.jsx +113 -0
- package/lib/chat/components/sidebar-history.js +115 -0
- package/lib/chat/components/sidebar-history.jsx +157 -0
- package/lib/chat/components/sidebar-user-nav.js +63 -0
- package/lib/chat/components/sidebar-user-nav.jsx +73 -0
- package/lib/chat/components/status-bar.js +39 -0
- package/lib/chat/components/status-bar.jsx +51 -0
- package/lib/chat/components/swarm-page.js +157 -0
- package/lib/chat/components/swarm-page.jsx +210 -0
- package/lib/chat/components/targets-page.js +376 -0
- package/lib/chat/components/targets-page.jsx +389 -0
- package/lib/chat/components/tool-call.js +86 -0
- package/lib/chat/components/tool-call.jsx +104 -0
- package/lib/chat/components/tool-panel.js +107 -0
- package/lib/chat/components/tool-panel.jsx +145 -0
- package/lib/chat/components/triggers-page.js +153 -0
- package/lib/chat/components/triggers-page.jsx +221 -0
- package/lib/chat/components/ui/confirm-dialog.js +53 -0
- package/lib/chat/components/ui/confirm-dialog.jsx +57 -0
- package/lib/chat/components/ui/dropdown-menu.js +98 -0
- package/lib/chat/components/ui/dropdown-menu.jsx +116 -0
- package/lib/chat/components/ui/rename-dialog.js +74 -0
- package/lib/chat/components/ui/rename-dialog.jsx +72 -0
- package/lib/chat/components/ui/scroll-area.js +13 -0
- package/lib/chat/components/ui/scroll-area.jsx +17 -0
- package/lib/chat/components/ui/separator.js +21 -0
- package/lib/chat/components/ui/separator.jsx +18 -0
- package/lib/chat/components/ui/sheet.js +75 -0
- package/lib/chat/components/ui/sheet.jsx +95 -0
- package/lib/chat/components/ui/sidebar.js +227 -0
- package/lib/chat/components/ui/sidebar.jsx +245 -0
- package/lib/chat/components/ui/tooltip.js +56 -0
- package/lib/chat/components/ui/tooltip.jsx +66 -0
- package/lib/chat/components/upgrade-dialog.js +151 -0
- package/lib/chat/components/upgrade-dialog.jsx +170 -0
- package/lib/chat/utils.js +11 -0
- package/lib/cron.js +246 -0
- package/lib/db/api-keys.js +163 -0
- package/lib/db/chats.js +145 -0
- package/lib/db/index.js +52 -0
- package/lib/db/notifications.js +99 -0
- package/lib/db/schema.js +145 -0
- package/lib/db/update-check.js +96 -0
- package/lib/db/users.js +89 -0
- package/lib/mcp/actions.js +104 -0
- package/lib/mcp/client.js +79 -0
- package/lib/mcp/handler.js +57 -0
- package/lib/mcp/server.js +165 -0
- package/lib/paths.js +46 -0
- package/lib/registry/actions.js +164 -0
- package/lib/registry/catalog.js +137 -0
- package/lib/registry/tools.js +71 -0
- package/lib/tools/create-job.js +99 -0
- package/lib/tools/github.js +217 -0
- package/lib/tools/openai.js +35 -0
- package/lib/tools/telegram.js +292 -0
- package/lib/triggers.js +118 -0
- package/lib/utils/render-md.js +102 -0
- package/package.json +103 -0
- package/setup/lib/auth.mjs +81 -0
- package/setup/lib/env.mjs +21 -0
- package/setup/lib/fs-utils.mjs +20 -0
- package/setup/lib/github.mjs +149 -0
- package/setup/lib/prerequisites.mjs +155 -0
- package/setup/lib/prompts.mjs +267 -0
- package/setup/lib/providers.mjs +48 -0
- package/setup/lib/sync.mjs +125 -0
- package/setup/lib/targets.mjs +45 -0
- package/setup/lib/telegram-verify.mjs +63 -0
- package/setup/lib/telegram.mjs +76 -0
- package/setup/setup-telegram.mjs +264 -0
- package/setup/setup.mjs +842 -0
- package/templates/.dockerignore +5 -0
- package/templates/.env.example +63 -0
- package/templates/.github/workflows/auto-merge.yml +117 -0
- package/templates/.github/workflows/build-image.yml +36 -0
- package/templates/.github/workflows/notify-job-failed.yml +64 -0
- package/templates/.github/workflows/notify-pr-complete.yml +119 -0
- package/templates/.github/workflows/rebuild-event-handler.yml +121 -0
- package/templates/.github/workflows/run-job.yml +89 -0
- package/templates/.github/workflows/upgrade-event-handler.yml +62 -0
- package/templates/.gitignore.template +45 -0
- package/templates/.pi/extensions/env-sanitizer/index.ts +48 -0
- package/templates/.pi/extensions/env-sanitizer/package.json +5 -0
- package/templates/CLAUDE.md +29 -0
- package/templates/CLAUDE.md.template +307 -0
- package/templates/app/api/[...thepopebot]/route.js +1 -0
- package/templates/app/api/auth/[...nextauth]/route.js +1 -0
- package/templates/app/chat/[chatId]/page.js +8 -0
- package/templates/app/chats/page.js +7 -0
- package/templates/app/components/ascii-logo.jsx +10 -0
- package/templates/app/components/login-form.jsx +92 -0
- package/templates/app/components/setup-form.jsx +82 -0
- package/templates/app/components/theme-provider.jsx +11 -0
- package/templates/app/components/theme-toggle.jsx +38 -0
- package/templates/app/components/ui/button.jsx +21 -0
- package/templates/app/components/ui/card.jsx +23 -0
- package/templates/app/components/ui/input.jsx +10 -0
- package/templates/app/components/ui/label.jsx +10 -0
- package/templates/app/crons/page.js +5 -0
- package/templates/app/findings/page.js +7 -0
- package/templates/app/globals.css +90 -0
- package/templates/app/layout.js +19 -0
- package/templates/app/login/page.js +15 -0
- package/templates/app/notifications/page.js +7 -0
- package/templates/app/page.js +7 -0
- package/templates/app/settings/crons/page.js +5 -0
- package/templates/app/settings/layout.js +7 -0
- package/templates/app/settings/mcp/page.js +5 -0
- package/templates/app/settings/page.js +5 -0
- package/templates/app/settings/secrets/page.js +5 -0
- package/templates/app/settings/triggers/page.js +5 -0
- package/templates/app/stream/chat/route.js +1 -0
- package/templates/app/swarm/page.js +7 -0
- package/templates/app/targets/page.js +7 -0
- package/templates/app/toolbox/page.js +7 -0
- package/templates/app/triggers/page.js +5 -0
- package/templates/config/AGENT.md +34 -0
- package/templates/config/CRONS.json +56 -0
- package/templates/config/EVENT_HANDLER.md +224 -0
- package/templates/config/HEARTBEAT.md +3 -0
- package/templates/config/JOB_SUMMARY.md +130 -0
- package/templates/config/MCP_SERVERS.json +1 -0
- package/templates/config/SKILL_BUILDING_GUIDE.md +90 -0
- package/templates/config/SOUL.md +17 -0
- package/templates/config/TRIGGERS.json +58 -0
- package/templates/docker/event-handler/Dockerfile +20 -0
- package/templates/docker/event-handler/ecosystem.config.cjs +8 -0
- package/templates/docker/job-claude-code/Dockerfile +34 -0
- package/templates/docker/job-claude-code/entrypoint.sh +139 -0
- package/templates/docker/job-pi-coding-agent/Dockerfile +44 -0
- package/templates/docker/job-pi-coding-agent/entrypoint.sh +163 -0
- package/templates/docker-compose.yml +63 -0
- package/templates/instrumentation.js +6 -0
- package/templates/middleware.js +1 -0
- package/templates/next.config.mjs +3 -0
- package/templates/postcss.config.mjs +5 -0
- package/templates/skills/LICENSE +21 -0
- package/templates/skills/README.md +119 -0
- package/templates/skills/brave-search/SKILL.md +79 -0
- package/templates/skills/brave-search/content.js +86 -0
- package/templates/skills/brave-search/package-lock.json +621 -0
- package/templates/skills/brave-search/package.json +14 -0
- package/templates/skills/brave-search/search.js +199 -0
- package/templates/skills/browser-tools/SKILL.md +196 -0
- package/templates/skills/browser-tools/browser-content.js +103 -0
- package/templates/skills/browser-tools/browser-cookies.js +35 -0
- package/templates/skills/browser-tools/browser-eval.js +53 -0
- package/templates/skills/browser-tools/browser-hn-scraper.js +108 -0
- package/templates/skills/browser-tools/browser-nav.js +44 -0
- package/templates/skills/browser-tools/browser-pick.js +162 -0
- package/templates/skills/browser-tools/browser-screenshot.js +34 -0
- package/templates/skills/browser-tools/browser-start.js +87 -0
- package/templates/skills/browser-tools/package-lock.json +2556 -0
- package/templates/skills/browser-tools/package.json +19 -0
- package/templates/skills/llm-secrets/SKILL.md +34 -0
- package/templates/skills/llm-secrets/llm-secrets.js +33 -0
- package/templates/skills/modify-self/SKILL.md +12 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "7",
|
|
3
|
+
"dialect": "sqlite",
|
|
4
|
+
"entries": [
|
|
5
|
+
{
|
|
6
|
+
"idx": 0,
|
|
7
|
+
"version": "6",
|
|
8
|
+
"when": 1771448716757,
|
|
9
|
+
"tag": "0000_initial",
|
|
10
|
+
"breakpoints": true
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"idx": 1,
|
|
14
|
+
"version": "6",
|
|
15
|
+
"when": 1772228400000,
|
|
16
|
+
"tag": "0001_bounty_and_registry",
|
|
17
|
+
"breakpoints": true
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"idx": 2,
|
|
21
|
+
"version": "6",
|
|
22
|
+
"when": 1772315400000,
|
|
23
|
+
"tag": "0002_sync_columns",
|
|
24
|
+
"breakpoints": true
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"idx": 3,
|
|
28
|
+
"version": "6",
|
|
29
|
+
"when": 1772287739089,
|
|
30
|
+
"tag": "0003_graceful_bloodscream",
|
|
31
|
+
"breakpoints": true
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { sqliteTable, AnySQLiteColumn, text, integer, uniqueIndex } from "drizzle-orm/sqlite-core"
|
|
2
|
+
import { sql } from "drizzle-orm"
|
|
3
|
+
|
|
4
|
+
export const programs = sqliteTable("programs", {
|
|
5
|
+
id: text().primaryKey().notNull(),
|
|
6
|
+
name: text().notNull(),
|
|
7
|
+
platform: text().default("custom").notNull(),
|
|
8
|
+
url: text(),
|
|
9
|
+
scopeUrl: text("scope_url"),
|
|
10
|
+
minBounty: integer("min_bounty"),
|
|
11
|
+
maxBounty: integer("max_bounty"),
|
|
12
|
+
status: text().default("active").notNull(),
|
|
13
|
+
notes: text(),
|
|
14
|
+
createdAt: integer("created_at").notNull(),
|
|
15
|
+
updatedAt: integer("updated_at").notNull(),
|
|
16
|
+
syncHandle: text("sync_handle"),
|
|
17
|
+
lastSyncedAt: integer("last_synced_at"),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export const targets = sqliteTable("targets", {
|
|
21
|
+
id: text().primaryKey().notNull(),
|
|
22
|
+
programId: text("program_id"),
|
|
23
|
+
type: text().default("domain").notNull(),
|
|
24
|
+
value: text().notNull(),
|
|
25
|
+
status: text().default("in_scope").notNull(),
|
|
26
|
+
technologies: text(),
|
|
27
|
+
notes: text(),
|
|
28
|
+
lastScannedAt: integer("last_scanned_at"),
|
|
29
|
+
createdAt: integer("created_at").notNull(),
|
|
30
|
+
updatedAt: integer("updated_at").notNull(),
|
|
31
|
+
syncSource: text("sync_source"),
|
|
32
|
+
syncProgramHandle: text("sync_program_handle"),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
export const findings = sqliteTable("findings", {
|
|
36
|
+
id: text().primaryKey().notNull(),
|
|
37
|
+
targetId: text("target_id"),
|
|
38
|
+
title: text().notNull(),
|
|
39
|
+
severity: text().default("info").notNull(),
|
|
40
|
+
type: text().notNull(),
|
|
41
|
+
status: text().default("new").notNull(),
|
|
42
|
+
description: text(),
|
|
43
|
+
stepsToReproduce: text("steps_to_reproduce"),
|
|
44
|
+
impact: text(),
|
|
45
|
+
evidence: text(),
|
|
46
|
+
bountyAmount: integer("bounty_amount"),
|
|
47
|
+
reportUrl: text("report_url"),
|
|
48
|
+
agentId: text("agent_id"),
|
|
49
|
+
toolId: text("tool_id"),
|
|
50
|
+
rawOutput: text("raw_output"),
|
|
51
|
+
reportedAt: integer("reported_at"),
|
|
52
|
+
createdAt: integer("created_at").notNull(),
|
|
53
|
+
updatedAt: integer("updated_at").notNull(),
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
export const tools = sqliteTable("tools", {
|
|
57
|
+
id: text().primaryKey().notNull(),
|
|
58
|
+
catalogId: text("catalog_id"),
|
|
59
|
+
name: text().notNull(),
|
|
60
|
+
slug: text().notNull(),
|
|
61
|
+
category: text().notNull(),
|
|
62
|
+
description: text(),
|
|
63
|
+
dockerImage: text("docker_image"),
|
|
64
|
+
installCmd: text("install_cmd"),
|
|
65
|
+
sourceUrl: text("source_url"),
|
|
66
|
+
version: text(),
|
|
67
|
+
installed: integer().default(0).notNull(),
|
|
68
|
+
enabled: integer().default(1).notNull(),
|
|
69
|
+
config: text(),
|
|
70
|
+
mcpServerId: text("mcp_server_id"),
|
|
71
|
+
createdAt: integer("created_at").notNull(),
|
|
72
|
+
updatedAt: integer("updated_at").notNull(),
|
|
73
|
+
},
|
|
74
|
+
(table) => [
|
|
75
|
+
uniqueIndex("tools_slug_unique").on(table.slug),
|
|
76
|
+
]);
|
|
77
|
+
|
|
78
|
+
export const dockerContainers = sqliteTable("docker_containers", {
|
|
79
|
+
id: text().primaryKey().notNull(),
|
|
80
|
+
toolId: text("tool_id"),
|
|
81
|
+
containerId: text("container_id"),
|
|
82
|
+
imageName: text("image_name").notNull(),
|
|
83
|
+
status: text().default("created").notNull(),
|
|
84
|
+
agentId: text("agent_id"),
|
|
85
|
+
ports: text(),
|
|
86
|
+
env: text(),
|
|
87
|
+
logs: text(),
|
|
88
|
+
createdAt: integer("created_at").notNull(),
|
|
89
|
+
stoppedAt: integer("stopped_at"),
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
export const chats = sqliteTable("chats", {
|
|
93
|
+
id: text().primaryKey().notNull(),
|
|
94
|
+
userId: text("user_id").notNull(),
|
|
95
|
+
title: text().default("New Chat").notNull(),
|
|
96
|
+
starred: integer().default(0).notNull(),
|
|
97
|
+
createdAt: integer("created_at").notNull(),
|
|
98
|
+
updatedAt: integer("updated_at").notNull(),
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
export const messages = sqliteTable("messages", {
|
|
102
|
+
id: text().primaryKey().notNull(),
|
|
103
|
+
chatId: text("chat_id").notNull(),
|
|
104
|
+
role: text().notNull(),
|
|
105
|
+
content: text().notNull(),
|
|
106
|
+
createdAt: integer("created_at").notNull(),
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
export const notifications = sqliteTable("notifications", {
|
|
110
|
+
id: text().primaryKey().notNull(),
|
|
111
|
+
notification: text().notNull(),
|
|
112
|
+
payload: text().notNull(),
|
|
113
|
+
read: integer().default(0).notNull(),
|
|
114
|
+
createdAt: integer("created_at").notNull(),
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
export const settings = sqliteTable("settings", {
|
|
118
|
+
id: text().primaryKey().notNull(),
|
|
119
|
+
type: text().notNull(),
|
|
120
|
+
key: text().notNull(),
|
|
121
|
+
value: text().notNull(),
|
|
122
|
+
createdBy: text("created_by"),
|
|
123
|
+
createdAt: integer("created_at").notNull(),
|
|
124
|
+
updatedAt: integer("updated_at").notNull(),
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
export const subscriptions = sqliteTable("subscriptions", {
|
|
128
|
+
id: text().primaryKey().notNull(),
|
|
129
|
+
platform: text().notNull(),
|
|
130
|
+
channelId: text("channel_id").notNull(),
|
|
131
|
+
createdAt: integer("created_at").notNull(),
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
export const users = sqliteTable("users", {
|
|
135
|
+
id: text().primaryKey().notNull(),
|
|
136
|
+
email: text().notNull(),
|
|
137
|
+
passwordHash: text("password_hash").notNull(),
|
|
138
|
+
role: text().default("admin").notNull(),
|
|
139
|
+
createdAt: integer("created_at").notNull(),
|
|
140
|
+
updatedAt: integer("updated_at").notNull(),
|
|
141
|
+
},
|
|
142
|
+
(table) => [
|
|
143
|
+
uniqueIndex("users_email_unique").on(table.email),
|
|
144
|
+
]);
|
|
145
|
+
|
package/lib/actions.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { exec } from 'child_process';
|
|
2
|
+
import { promisify } from 'util';
|
|
3
|
+
import { createJob } from './tools/create-job.js';
|
|
4
|
+
|
|
5
|
+
const execAsync = promisify(exec);
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Execute a single action
|
|
9
|
+
* @param {Object} action - { type, job, command, url, method, headers, vars } (type: agent|command|webhook)
|
|
10
|
+
* @param {Object} opts - { cwd, data }
|
|
11
|
+
* @returns {Promise<string>} Result description for logging
|
|
12
|
+
*/
|
|
13
|
+
async function executeAction(action, opts = {}) {
|
|
14
|
+
const type = action.type || 'agent';
|
|
15
|
+
|
|
16
|
+
if (type === 'command') {
|
|
17
|
+
const { stdout, stderr } = await execAsync(action.command, {
|
|
18
|
+
cwd: opts.cwd,
|
|
19
|
+
timeout: 30000,
|
|
20
|
+
});
|
|
21
|
+
return (stdout || stderr || '').trim();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (type === 'webhook') {
|
|
25
|
+
const method = (action.method || 'POST').toUpperCase();
|
|
26
|
+
const headers = { 'Content-Type': 'application/json', ...action.headers };
|
|
27
|
+
const fetchOpts = { method, headers };
|
|
28
|
+
|
|
29
|
+
if (method !== 'GET') {
|
|
30
|
+
const body = { ...action.vars };
|
|
31
|
+
if (opts.data) body.data = opts.data;
|
|
32
|
+
fetchOpts.body = JSON.stringify(body);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const res = await fetch(action.url, fetchOpts);
|
|
36
|
+
return `${method} ${action.url} → ${res.status}`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Default: agent
|
|
40
|
+
const options = {};
|
|
41
|
+
if (action.llm_provider) options.llmProvider = action.llm_provider;
|
|
42
|
+
if (action.llm_model) options.llmModel = action.llm_model;
|
|
43
|
+
const result = await createJob(action.job, options);
|
|
44
|
+
return `job ${result.job_id} — ${result.title}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export { executeAction };
|
package/lib/agents.js
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { agentsDir } from './paths.js';
|
|
4
|
+
|
|
5
|
+
const SKIP_DIRS = ['shared', '_template', 'node_modules'];
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Parse flat YAML (key: value per line, simple arrays with [a, b]).
|
|
9
|
+
* No external yaml dependency needed — CONFIG.yaml files are flat key-value.
|
|
10
|
+
*/
|
|
11
|
+
function parseSimpleYaml(content) {
|
|
12
|
+
const result = {};
|
|
13
|
+
for (const line of content.split('\n')) {
|
|
14
|
+
const trimmed = line.split('#')[0].trim(); // strip comments
|
|
15
|
+
if (!trimmed || trimmed.startsWith('-')) continue;
|
|
16
|
+
|
|
17
|
+
const colonIdx = trimmed.indexOf(':');
|
|
18
|
+
if (colonIdx === -1) continue;
|
|
19
|
+
|
|
20
|
+
const key = trimmed.slice(0, colonIdx).trim();
|
|
21
|
+
let value = trimmed.slice(colonIdx + 1).trim();
|
|
22
|
+
|
|
23
|
+
// Parse arrays: [a, b, c]
|
|
24
|
+
if (value.startsWith('[') && value.endsWith(']')) {
|
|
25
|
+
value = value.slice(1, -1).split(',').map(v => v.trim()).filter(Boolean);
|
|
26
|
+
}
|
|
27
|
+
// Parse numbers
|
|
28
|
+
else if (/^\d+(\.\d+)?$/.test(value)) {
|
|
29
|
+
value = parseFloat(value);
|
|
30
|
+
}
|
|
31
|
+
// Parse booleans
|
|
32
|
+
else if (value === 'true') value = true;
|
|
33
|
+
else if (value === 'false') value = false;
|
|
34
|
+
|
|
35
|
+
result[key] = value;
|
|
36
|
+
}
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Parse IDENTITY.md into structured fields.
|
|
42
|
+
* Supports multiple formats:
|
|
43
|
+
* - Single-line: "Name: X. Codename: Y. Role: Z. Specialization: W."
|
|
44
|
+
* - Multi-line: "Name: X\nCodename: Y\nRole: Z\n..."
|
|
45
|
+
* - Markdown: "**Codename:** X\n**Role:** Y\n..."
|
|
46
|
+
*/
|
|
47
|
+
function parseIdentity(content) {
|
|
48
|
+
const identity = {};
|
|
49
|
+
const text = content.trim();
|
|
50
|
+
const fields = ['name', 'codename', 'role', 'specialization'];
|
|
51
|
+
|
|
52
|
+
// Extract each field by scanning the full text with a regex
|
|
53
|
+
// This handles both single-line ("Name: X. Codename: Y.") and multi-line formats
|
|
54
|
+
for (const field of fields) {
|
|
55
|
+
// Match "Field: value" or "**Field:** value" (case-insensitive)
|
|
56
|
+
// Value ends at next field label, period-space-capital, newline, or end of string
|
|
57
|
+
const pattern = new RegExp(
|
|
58
|
+
`\\*{0,2}${field}\\*{0,2}[:\\s]+\\s*(.+?)(?=\\s*\\.\\s*(?:Name|Codename|Role|Specialization)\\b|\\n|$)`,
|
|
59
|
+
'i'
|
|
60
|
+
);
|
|
61
|
+
const match = text.match(pattern);
|
|
62
|
+
if (match) {
|
|
63
|
+
identity[field] = match[1].trim().replace(/\.$/, '').replace(/^\*+\s*/, '').replace(/\s*\*+$/, '');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Fallback for markdown title: "# MAINTAINER — Code Quality Specialist"
|
|
68
|
+
if (!identity.codename) {
|
|
69
|
+
const titleMatch = text.match(/^#\s+(\w+)\s+[—–-]\s+(.+)/m);
|
|
70
|
+
if (titleMatch) {
|
|
71
|
+
identity.codename = titleMatch[1].trim();
|
|
72
|
+
if (!identity.role) identity.role = titleMatch[2].trim();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return identity;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Load a single agent profile by directory name.
|
|
81
|
+
* Returns null if the directory doesn't exist or is invalid.
|
|
82
|
+
*/
|
|
83
|
+
export function loadAgentProfile(agentDirName) {
|
|
84
|
+
const agentPath = path.join(agentsDir, agentDirName);
|
|
85
|
+
|
|
86
|
+
if (!fs.existsSync(agentPath)) return null;
|
|
87
|
+
const stat = fs.statSync(agentPath);
|
|
88
|
+
if (!stat.isDirectory()) return null;
|
|
89
|
+
|
|
90
|
+
const profile = { id: agentDirName };
|
|
91
|
+
|
|
92
|
+
// IDENTITY.md
|
|
93
|
+
const identityPath = path.join(agentPath, 'IDENTITY.md');
|
|
94
|
+
if (fs.existsSync(identityPath)) {
|
|
95
|
+
const identityContent = fs.readFileSync(identityPath, 'utf8');
|
|
96
|
+
Object.assign(profile, parseIdentity(identityContent));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// CONFIG.yaml
|
|
100
|
+
const configPath = path.join(agentPath, 'CONFIG.yaml');
|
|
101
|
+
if (fs.existsSync(configPath)) {
|
|
102
|
+
const configContent = fs.readFileSync(configPath, 'utf8');
|
|
103
|
+
profile.config = parseSimpleYaml(configContent);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// SOUL.md (raw content — used as system prompt)
|
|
107
|
+
const soulPath = path.join(agentPath, 'SOUL.md');
|
|
108
|
+
if (fs.existsSync(soulPath)) {
|
|
109
|
+
profile.soul = fs.readFileSync(soulPath, 'utf8');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// SKILLS.md
|
|
113
|
+
const skillsPath = path.join(agentPath, 'SKILLS.md');
|
|
114
|
+
if (fs.existsSync(skillsPath)) {
|
|
115
|
+
profile.skills = fs.readFileSync(skillsPath, 'utf8');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// TOOLS.md
|
|
119
|
+
const toolsPath = path.join(agentPath, 'TOOLS.md');
|
|
120
|
+
if (fs.existsSync(toolsPath)) {
|
|
121
|
+
profile.tools = fs.readFileSync(toolsPath, 'utf8');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// HEARTBEAT.md
|
|
125
|
+
const heartbeatPath = path.join(agentPath, 'HEARTBEAT.md');
|
|
126
|
+
if (fs.existsSync(heartbeatPath)) {
|
|
127
|
+
profile.heartbeat = fs.readFileSync(heartbeatPath, 'utf8');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return profile;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Discover all agent profiles from agents/ directory.
|
|
135
|
+
* Returns array of profile objects with id, name, codename, role, etc.
|
|
136
|
+
*/
|
|
137
|
+
export function discoverAgents() {
|
|
138
|
+
if (!fs.existsSync(agentsDir)) return [];
|
|
139
|
+
|
|
140
|
+
const entries = fs.readdirSync(agentsDir, { withFileTypes: true });
|
|
141
|
+
const agents = [];
|
|
142
|
+
|
|
143
|
+
for (const entry of entries) {
|
|
144
|
+
if (!entry.isDirectory()) continue;
|
|
145
|
+
if (SKIP_DIRS.includes(entry.name)) continue;
|
|
146
|
+
|
|
147
|
+
const profile = loadAgentProfile(entry.name);
|
|
148
|
+
if (profile) agents.push(profile);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return agents;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Get a bullet-list summary of all discovered agents (for {{agents}} variable).
|
|
156
|
+
*/
|
|
157
|
+
export function loadAgentDescriptions() {
|
|
158
|
+
const agents = discoverAgents();
|
|
159
|
+
if (agents.length === 0) return 'No agent profiles configured.';
|
|
160
|
+
|
|
161
|
+
return agents.map(a => {
|
|
162
|
+
const name = a.codename || a.name || a.id;
|
|
163
|
+
const role = a.role || 'general';
|
|
164
|
+
return `- **${name}**: ${role}`;
|
|
165
|
+
}).join('\n');
|
|
166
|
+
}
|
package/lib/ai/agent.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { createReactAgent } from '@langchain/langgraph/prebuilt';
|
|
2
|
+
import { SystemMessage } from '@langchain/core/messages';
|
|
3
|
+
import { createModel } from './model.js';
|
|
4
|
+
import { createJobTool, getJobStatusTool, getSystemTechnicalSpecsTool, getSkillBuildingGuideTool } from './tools.js';
|
|
5
|
+
import { SqliteSaver } from '@langchain/langgraph-checkpoint-sqlite';
|
|
6
|
+
import { eventHandlerMd, thepopebotDb } from '../paths.js';
|
|
7
|
+
import { render_md } from '../utils/render-md.js';
|
|
8
|
+
import { loadAgentProfile } from '../agents.js';
|
|
9
|
+
|
|
10
|
+
let _agent = null;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Get or create the LangGraph agent singleton.
|
|
14
|
+
* Uses createReactAgent which handles the tool loop automatically.
|
|
15
|
+
* Prompt is a function so {{datetime}} resolves fresh each invocation.
|
|
16
|
+
*/
|
|
17
|
+
export async function getAgent() {
|
|
18
|
+
if (!_agent) {
|
|
19
|
+
const model = await createModel();
|
|
20
|
+
let mcpTools = [];
|
|
21
|
+
try {
|
|
22
|
+
const { loadMcpTools } = await import('../mcp/client.js');
|
|
23
|
+
mcpTools = await loadMcpTools();
|
|
24
|
+
} catch (err) {
|
|
25
|
+
console.error('[getAgent] Failed to load MCP tools:', err.message);
|
|
26
|
+
}
|
|
27
|
+
const tools = [createJobTool, getJobStatusTool, getSystemTechnicalSpecsTool, getSkillBuildingGuideTool, ...mcpTools];
|
|
28
|
+
const checkpointer = SqliteSaver.fromConnString(thepopebotDb);
|
|
29
|
+
|
|
30
|
+
_agent = createReactAgent({
|
|
31
|
+
llm: model,
|
|
32
|
+
tools,
|
|
33
|
+
checkpointSaver: checkpointer,
|
|
34
|
+
prompt: (state) => [new SystemMessage(render_md(eventHandlerMd)), ...state.messages],
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
return _agent;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Reset the agent singleton (e.g., when config changes).
|
|
42
|
+
*/
|
|
43
|
+
export function resetAgent() {
|
|
44
|
+
_agent = null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Per-profile agent instances
|
|
48
|
+
const _agents = new Map();
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Get or create an agent for a specific agent profile.
|
|
52
|
+
* Uses the agent's SOUL.md as the system prompt instead of EVENT_HANDLER.md.
|
|
53
|
+
* Falls back to getAgent() if no profile found.
|
|
54
|
+
*
|
|
55
|
+
* @param {string} profileName - Directory name of the agent profile (e.g. 'recon-scout')
|
|
56
|
+
*/
|
|
57
|
+
export async function getAgentForProfile(profileName) {
|
|
58
|
+
if (_agents.has(profileName)) return _agents.get(profileName);
|
|
59
|
+
|
|
60
|
+
const profile = loadAgentProfile(profileName);
|
|
61
|
+
if (!profile || !profile.soul) return getAgent(); // fallback to default
|
|
62
|
+
|
|
63
|
+
const model = await createModel();
|
|
64
|
+
let mcpTools = [];
|
|
65
|
+
try {
|
|
66
|
+
const { loadMcpTools } = await import('../mcp/client.js');
|
|
67
|
+
mcpTools = await loadMcpTools();
|
|
68
|
+
} catch (err) {
|
|
69
|
+
console.error(`[getAgentForProfile:${profileName}] Failed to load MCP tools:`, err.message);
|
|
70
|
+
}
|
|
71
|
+
const tools = [createJobTool, getJobStatusTool, getSystemTechnicalSpecsTool, getSkillBuildingGuideTool, ...mcpTools];
|
|
72
|
+
const checkpointer = SqliteSaver.fromConnString(thepopebotDb);
|
|
73
|
+
|
|
74
|
+
// Build the system prompt from the agent's SOUL + optional SKILLS + TOOLS
|
|
75
|
+
const systemParts = [profile.soul];
|
|
76
|
+
if (profile.skills) systemParts.push('\n\n## Skills\n\n' + profile.skills);
|
|
77
|
+
if (profile.tools) systemParts.push('\n\n## Tools\n\n' + profile.tools);
|
|
78
|
+
const systemPrompt = systemParts.join('');
|
|
79
|
+
|
|
80
|
+
const agent = createReactAgent({
|
|
81
|
+
llm: model,
|
|
82
|
+
tools,
|
|
83
|
+
checkpointSaver: checkpointer,
|
|
84
|
+
prompt: (state) => [new SystemMessage(systemPrompt), ...state.messages],
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
_agents.set(profileName, agent);
|
|
88
|
+
return agent;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Reset a specific profile agent (e.g., when config changes).
|
|
93
|
+
*/
|
|
94
|
+
export function resetAgentForProfile(profileName) {
|
|
95
|
+
_agents.delete(profileName);
|
|
96
|
+
}
|