gigaclaw 1.4.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.
Files changed (249) hide show
  1. package/LICENSE +26 -0
  2. package/README.md +237 -0
  3. package/api/CLAUDE.md +19 -0
  4. package/api/index.js +265 -0
  5. package/bin/cli.js +823 -0
  6. package/bin/local.sh +85 -0
  7. package/bin/postinstall.js +63 -0
  8. package/config/index.js +26 -0
  9. package/config/instrumentation.js +62 -0
  10. package/drizzle/0000_initial.sql +52 -0
  11. package/drizzle/0001_nostalgic_sersi.sql +11 -0
  12. package/drizzle/0002_black_daimon_hellstrom.sql +19 -0
  13. package/drizzle/0003_rename_code_workspaces.sql +5 -0
  14. package/drizzle/meta/0000_snapshot.json +321 -0
  15. package/drizzle/meta/0001_snapshot.json +390 -0
  16. package/drizzle/meta/0002_snapshot.json +411 -0
  17. package/drizzle/meta/0003_snapshot.json +419 -0
  18. package/drizzle/meta/_journal.json +34 -0
  19. package/lib/actions.js +44 -0
  20. package/lib/ai/agent.js +86 -0
  21. package/lib/ai/index.js +342 -0
  22. package/lib/ai/model.js +180 -0
  23. package/lib/ai/tools.js +269 -0
  24. package/lib/ai/web-search.js +42 -0
  25. package/lib/auth/actions.js +28 -0
  26. package/lib/auth/config.js +27 -0
  27. package/lib/auth/edge-config.js +27 -0
  28. package/lib/auth/index.js +27 -0
  29. package/lib/auth/middleware.js +62 -0
  30. package/lib/channels/base.js +56 -0
  31. package/lib/channels/index.js +15 -0
  32. package/lib/channels/telegram.js +148 -0
  33. package/lib/chat/actions.js +579 -0
  34. package/lib/chat/api.js +140 -0
  35. package/lib/chat/components/app-sidebar.js +213 -0
  36. package/lib/chat/components/app-sidebar.jsx +279 -0
  37. package/lib/chat/components/chat-header.js +192 -0
  38. package/lib/chat/components/chat-header.jsx +223 -0
  39. package/lib/chat/components/chat-input.js +236 -0
  40. package/lib/chat/components/chat-input.jsx +249 -0
  41. package/lib/chat/components/chat-nav-context.js +11 -0
  42. package/lib/chat/components/chat-nav-context.jsx +11 -0
  43. package/lib/chat/components/chat-page.js +99 -0
  44. package/lib/chat/components/chat-page.jsx +121 -0
  45. package/lib/chat/components/chat.js +153 -0
  46. package/lib/chat/components/chat.jsx +199 -0
  47. package/lib/chat/components/chats-page.js +367 -0
  48. package/lib/chat/components/chats-page.jsx +394 -0
  49. package/lib/chat/components/code-mode-toggle.js +132 -0
  50. package/lib/chat/components/code-mode-toggle.jsx +163 -0
  51. package/lib/chat/components/crons-page.js +172 -0
  52. package/lib/chat/components/crons-page.jsx +244 -0
  53. package/lib/chat/components/greeting.js +11 -0
  54. package/lib/chat/components/greeting.jsx +16 -0
  55. package/lib/chat/components/icons.js +805 -0
  56. package/lib/chat/components/icons.jsx +751 -0
  57. package/lib/chat/components/index.js +20 -0
  58. package/lib/chat/components/message.js +363 -0
  59. package/lib/chat/components/message.jsx +422 -0
  60. package/lib/chat/components/messages.js +65 -0
  61. package/lib/chat/components/messages.jsx +74 -0
  62. package/lib/chat/components/notifications-page.js +56 -0
  63. package/lib/chat/components/notifications-page.jsx +87 -0
  64. package/lib/chat/components/page-layout.js +21 -0
  65. package/lib/chat/components/page-layout.jsx +28 -0
  66. package/lib/chat/components/pull-requests-page.js +103 -0
  67. package/lib/chat/components/pull-requests-page.jsx +113 -0
  68. package/lib/chat/components/settings-layout.js +39 -0
  69. package/lib/chat/components/settings-layout.jsx +53 -0
  70. package/lib/chat/components/settings-secrets-page.js +216 -0
  71. package/lib/chat/components/settings-secrets-page.jsx +264 -0
  72. package/lib/chat/components/sidebar-history-item.js +138 -0
  73. package/lib/chat/components/sidebar-history-item.jsx +119 -0
  74. package/lib/chat/components/sidebar-history.js +167 -0
  75. package/lib/chat/components/sidebar-history.jsx +220 -0
  76. package/lib/chat/components/sidebar-user-nav.js +61 -0
  77. package/lib/chat/components/sidebar-user-nav.jsx +77 -0
  78. package/lib/chat/components/swarm-page.js +157 -0
  79. package/lib/chat/components/swarm-page.jsx +210 -0
  80. package/lib/chat/components/tool-call.js +89 -0
  81. package/lib/chat/components/tool-call.jsx +107 -0
  82. package/lib/chat/components/triggers-page.js +153 -0
  83. package/lib/chat/components/triggers-page.jsx +221 -0
  84. package/lib/chat/components/ui/combobox.js +98 -0
  85. package/lib/chat/components/ui/combobox.jsx +114 -0
  86. package/lib/chat/components/ui/confirm-dialog.js +53 -0
  87. package/lib/chat/components/ui/confirm-dialog.jsx +57 -0
  88. package/lib/chat/components/ui/dropdown-menu.js +194 -0
  89. package/lib/chat/components/ui/dropdown-menu.jsx +215 -0
  90. package/lib/chat/components/ui/rename-dialog.js +78 -0
  91. package/lib/chat/components/ui/rename-dialog.jsx +74 -0
  92. package/lib/chat/components/ui/scroll-area.js +13 -0
  93. package/lib/chat/components/ui/scroll-area.jsx +17 -0
  94. package/lib/chat/components/ui/separator.js +21 -0
  95. package/lib/chat/components/ui/separator.jsx +18 -0
  96. package/lib/chat/components/ui/sheet.js +75 -0
  97. package/lib/chat/components/ui/sheet.jsx +95 -0
  98. package/lib/chat/components/ui/sidebar.js +228 -0
  99. package/lib/chat/components/ui/sidebar.jsx +246 -0
  100. package/lib/chat/components/ui/tooltip.js +56 -0
  101. package/lib/chat/components/ui/tooltip.jsx +66 -0
  102. package/lib/chat/components/upgrade-dialog.js +151 -0
  103. package/lib/chat/components/upgrade-dialog.jsx +170 -0
  104. package/lib/chat/utils.js +11 -0
  105. package/lib/code/actions.js +153 -0
  106. package/lib/code/code-page.js +22 -0
  107. package/lib/code/code-page.jsx +25 -0
  108. package/lib/code/index.js +1 -0
  109. package/lib/code/terminal-view.js +201 -0
  110. package/lib/code/terminal-view.jsx +224 -0
  111. package/lib/code/ws-proxy.js +80 -0
  112. package/lib/cron.js +246 -0
  113. package/lib/db/api-keys.js +163 -0
  114. package/lib/db/chats.js +168 -0
  115. package/lib/db/code-workspaces.js +110 -0
  116. package/lib/db/index.js +52 -0
  117. package/lib/db/notifications.js +99 -0
  118. package/lib/db/schema.js +66 -0
  119. package/lib/db/update-check.js +96 -0
  120. package/lib/db/users.js +89 -0
  121. package/lib/paths.js +42 -0
  122. package/lib/tools/create-job.js +97 -0
  123. package/lib/tools/docker.js +146 -0
  124. package/lib/tools/github.js +271 -0
  125. package/lib/tools/openai.js +35 -0
  126. package/lib/tools/telegram.js +292 -0
  127. package/lib/triggers.js +104 -0
  128. package/lib/utils/render-md.js +111 -0
  129. package/package.json +118 -0
  130. package/setup/lib/auth.mjs +81 -0
  131. package/setup/lib/env.mjs +21 -0
  132. package/setup/lib/fs-utils.mjs +20 -0
  133. package/setup/lib/github.mjs +149 -0
  134. package/setup/lib/prerequisites.mjs +155 -0
  135. package/setup/lib/prompts.mjs +267 -0
  136. package/setup/lib/providers.mjs +105 -0
  137. package/setup/lib/sync.mjs +125 -0
  138. package/setup/lib/targets.mjs +45 -0
  139. package/setup/lib/telegram-verify.mjs +63 -0
  140. package/setup/lib/telegram.mjs +76 -0
  141. package/setup/setup-cloud.mjs +833 -0
  142. package/setup/setup-local.mjs +377 -0
  143. package/setup/setup-telegram.mjs +265 -0
  144. package/setup/setup.mjs +87 -0
  145. package/templates/.dockerignore +5 -0
  146. package/templates/.env.example +104 -0
  147. package/templates/.github/workflows/auto-merge.yml +117 -0
  148. package/templates/.github/workflows/notify-job-failed.yml +64 -0
  149. package/templates/.github/workflows/notify-pr-complete.yml +119 -0
  150. package/templates/.github/workflows/rebuild-event-handler.yml +121 -0
  151. package/templates/.github/workflows/run-job.yml +89 -0
  152. package/templates/.github/workflows/upgrade-event-handler.yml +62 -0
  153. package/templates/.gitignore.template +45 -0
  154. package/templates/.pi/extensions/env-sanitizer/index.ts +48 -0
  155. package/templates/.pi/extensions/env-sanitizer/package.json +5 -0
  156. package/templates/CLAUDE.md +29 -0
  157. package/templates/CLAUDE.md.template +308 -0
  158. package/templates/app/api/[...gigaclaw]/route.js +1 -0
  159. package/templates/app/api/auth/[...nextauth]/route.js +1 -0
  160. package/templates/app/chat/[chatId]/page.js +9 -0
  161. package/templates/app/chats/page.js +7 -0
  162. package/templates/app/code/[codeWorkspaceId]/page.js +9 -0
  163. package/templates/app/components/ascii-logo.jsx +12 -0
  164. package/templates/app/components/login-form.jsx +92 -0
  165. package/templates/app/components/setup-form.jsx +82 -0
  166. package/templates/app/components/theme-provider.jsx +11 -0
  167. package/templates/app/components/theme-toggle.jsx +38 -0
  168. package/templates/app/components/ui/button.jsx +21 -0
  169. package/templates/app/components/ui/card.jsx +23 -0
  170. package/templates/app/components/ui/input.jsx +10 -0
  171. package/templates/app/components/ui/label.jsx +10 -0
  172. package/templates/app/crons/page.js +5 -0
  173. package/templates/app/globals.css +90 -0
  174. package/templates/app/layout.js +33 -0
  175. package/templates/app/login/page.js +15 -0
  176. package/templates/app/notifications/page.js +7 -0
  177. package/templates/app/page.js +7 -0
  178. package/templates/app/pull-requests/page.js +7 -0
  179. package/templates/app/settings/crons/page.js +5 -0
  180. package/templates/app/settings/layout.js +7 -0
  181. package/templates/app/settings/page.js +5 -0
  182. package/templates/app/settings/secrets/page.js +5 -0
  183. package/templates/app/settings/triggers/page.js +5 -0
  184. package/templates/app/stream/chat/route.js +1 -0
  185. package/templates/app/swarm/page.js +7 -0
  186. package/templates/app/triggers/page.js +5 -0
  187. package/templates/config/CODE_PLANNING.md +14 -0
  188. package/templates/config/CRONS.json +56 -0
  189. package/templates/config/HEARTBEAT.md +3 -0
  190. package/templates/config/JOB_AGENT.md +30 -0
  191. package/templates/config/JOB_PLANNING.md +240 -0
  192. package/templates/config/JOB_SUMMARY.md +130 -0
  193. package/templates/config/SKILL_BUILDING_GUIDE.md +96 -0
  194. package/templates/config/SOUL.md +48 -0
  195. package/templates/config/TRIGGERS.json +58 -0
  196. package/templates/config/WEB_SEARCH_AVAILABLE.md +5 -0
  197. package/templates/config/WEB_SEARCH_UNAVAILABLE.md +3 -0
  198. package/templates/docker/claude-code-job/Dockerfile +34 -0
  199. package/templates/docker/claude-code-job/entrypoint.sh +149 -0
  200. package/templates/docker/claude-code-workspace/.tmux.conf +5 -0
  201. package/templates/docker/claude-code-workspace/Dockerfile +61 -0
  202. package/templates/docker/claude-code-workspace/entrypoint.sh +51 -0
  203. package/templates/docker/event-handler/Dockerfile +20 -0
  204. package/templates/docker/event-handler/ecosystem.config.cjs +7 -0
  205. package/templates/docker/pi-coding-agent-job/Dockerfile +51 -0
  206. package/templates/docker/pi-coding-agent-job/entrypoint.sh +164 -0
  207. package/templates/docker-compose.local.yml +78 -0
  208. package/templates/docker-compose.yml +64 -0
  209. package/templates/instrumentation.js +6 -0
  210. package/templates/middleware.js +23 -0
  211. package/templates/next.config.mjs +3 -0
  212. package/templates/postcss.config.mjs +5 -0
  213. package/templates/public/favicon.ico +0 -0
  214. package/templates/server.js +25 -0
  215. package/templates/skills/LICENSE +21 -0
  216. package/templates/skills/README.md +119 -0
  217. package/templates/skills/brave-search/SKILL.md +79 -0
  218. package/templates/skills/brave-search/content.js +86 -0
  219. package/templates/skills/brave-search/package-lock.json +621 -0
  220. package/templates/skills/brave-search/package.json +14 -0
  221. package/templates/skills/brave-search/search.js +199 -0
  222. package/templates/skills/browser-tools/SKILL.md +196 -0
  223. package/templates/skills/browser-tools/browser-content.js +103 -0
  224. package/templates/skills/browser-tools/browser-cookies.js +35 -0
  225. package/templates/skills/browser-tools/browser-eval.js +53 -0
  226. package/templates/skills/browser-tools/browser-hn-scraper.js +108 -0
  227. package/templates/skills/browser-tools/browser-nav.js +44 -0
  228. package/templates/skills/browser-tools/browser-pick.js +162 -0
  229. package/templates/skills/browser-tools/browser-screenshot.js +34 -0
  230. package/templates/skills/browser-tools/browser-start.js +87 -0
  231. package/templates/skills/browser-tools/package-lock.json +2556 -0
  232. package/templates/skills/browser-tools/package.json +19 -0
  233. package/templates/skills/google-docs/SKILL.md +23 -0
  234. package/templates/skills/google-docs/create.sh +69 -0
  235. package/templates/skills/google-drive/SKILL.md +47 -0
  236. package/templates/skills/google-drive/delete.sh +47 -0
  237. package/templates/skills/google-drive/download.sh +50 -0
  238. package/templates/skills/google-drive/list.sh +41 -0
  239. package/templates/skills/google-drive/upload.sh +76 -0
  240. package/templates/skills/kie-ai/SKILL.md +38 -0
  241. package/templates/skills/kie-ai/generate-image.sh +77 -0
  242. package/templates/skills/kie-ai/generate-video.sh +69 -0
  243. package/templates/skills/llm-secrets/SKILL.md +34 -0
  244. package/templates/skills/llm-secrets/llm-secrets.js +33 -0
  245. package/templates/skills/modify-self/SKILL.md +12 -0
  246. package/templates/skills/youtube-transcript/SKILL.md +41 -0
  247. package/templates/skills/youtube-transcript/package-lock.json +24 -0
  248. package/templates/skills/youtube-transcript/package.json +8 -0
  249. package/templates/skills/youtube-transcript/transcript.js +84 -0
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * TTY Guard — second line of defence against curl|bash stdin hijacking.
5
+ *
6
+ * When `npm run setup` is invoked from a script whose stdin is a pipe
7
+ * (e.g. curl | bash), process.stdin.isTTY is undefined and @clack/prompts
8
+ * exits immediately on the first keypress event (which is EOF).
9
+ *
10
+ * If /dev/tty is accessible we re-open it and reassign process.stdin so
11
+ * that @clack/prompts gets a real terminal regardless of how this script
12
+ * was launched. The primary fix lives in install.sh (exec < /dev/tty),
13
+ * but this guard handles cases where setup.mjs is called directly without
14
+ * going through install.sh.
15
+ */
16
+ import fs from 'fs';
17
+ import { Readable } from 'stream';
18
+
19
+ if (!process.stdin.isTTY) {
20
+ try {
21
+ const ttyFd = fs.openSync('/dev/tty', 'r+');
22
+ const ttyStream = new fs.ReadStream(null, { fd: ttyFd, autoClose: true });
23
+ // Monkey-patch the TTY flag so @clack/prompts enables raw mode
24
+ ttyStream.isTTY = true;
25
+ ttyStream.setRawMode = (mode) => {
26
+ try { fs.fstatSync(ttyFd); require('tty').ReadStream.prototype.setRawMode.call(ttyStream, mode); } catch (_) {}
27
+ };
28
+ process.stdin = ttyStream;
29
+ } catch (_) {
30
+ // /dev/tty not available (CI, Docker without TTY, etc.) — fall through;
31
+ // the prompts will still render but keyboard input may not work.
32
+ // In that case the user should run `npm run setup` directly in a terminal.
33
+ }
34
+ }
35
+
36
+ import chalk from 'chalk';
37
+ import * as clack from '@clack/prompts';
38
+
39
+ const logo = `
40
+ _______ ____ __
41
+ / ____(_)___ _____ _/ __ )____ / /_
42
+ / / __/ / __ \`/ __ \`/ __ / __ \\/ __/
43
+ / /_/ / / /_/ / /_/ / /_/ / /_/ / /_
44
+ \\____/_/\\__, /\\__,_/_____/\\____/\\__/
45
+ /____/
46
+ India's Autonomous AI Agent · Powered by Gignaati
47
+ `;
48
+
49
+ async function main() {
50
+ console.log(chalk.cyan(logo));
51
+
52
+ clack.intro('GigaBot Setup Wizard');
53
+
54
+ const mode = await clack.select({
55
+ message: 'How do you want to run GigaBot?',
56
+ options: [
57
+ {
58
+ value: 'cloud',
59
+ label: 'Cloud Mode',
60
+ hint: 'GitHub + ngrok + Telegram — full features, internet required',
61
+ },
62
+ {
63
+ value: 'local',
64
+ label: 'Local Mode',
65
+ hint: 'Ollama only — 100% offline, no Telegram or GitHub needed',
66
+ },
67
+ ],
68
+ });
69
+
70
+ if (clack.isCancel(mode)) {
71
+ clack.cancel('Setup cancelled.');
72
+ process.exit(0);
73
+ }
74
+
75
+ if (mode === 'cloud') {
76
+ const { run } = await import('./setup-cloud.mjs');
77
+ await run();
78
+ } else {
79
+ const { run } = await import('./setup-local.mjs');
80
+ await run();
81
+ }
82
+ }
83
+
84
+ main().catch((error) => {
85
+ clack.log.error(`Setup failed: ${error.message}`);
86
+ process.exit(1);
87
+ });
@@ -0,0 +1,5 @@
1
+ node_modules
2
+ .env
3
+ data/
4
+ logs/
5
+ .git
@@ -0,0 +1,104 @@
1
+ # ============================================================
2
+ # Giga Bot Configuration — Powered by Gignaati
3
+ # ============================================================
4
+ # Copy this file to .env and fill in your values.
5
+ # NEVER commit the actual .env file with real secrets!
6
+ # Run the setup wizard to auto-fill most of these:
7
+ # npx gigabot setup
8
+ # ============================================================
9
+
10
+ # ─── Authentication ──────────────────────────────────────────
11
+ # Auth.js secret for session encryption (auto-generated by setup wizard)
12
+ # To generate manually: openssl rand -base64 32
13
+ AUTH_SECRET=
14
+ AUTH_TRUST_HOST=true
15
+
16
+ # ─── App URL ─────────────────────────────────────────────────
17
+ # Public URL for webhooks, Telegram, and Traefik hostname
18
+ # e.g., https://mybot.example.com or https://abc123.ngrok.io
19
+ APP_URL=
20
+ APP_HOSTNAME=
21
+
22
+ # ─── GitHub ──────────────────────────────────────────────────
23
+ # GitHub Personal Access Token (needs repo + workflow scopes)
24
+ # Create at: https://github.com/settings/tokens/new
25
+ GH_TOKEN=ghp_your_token_here
26
+ GH_OWNER=your_github_username
27
+ GH_REPO=your_repo_name
28
+ GH_WEBHOOK_SECRET=
29
+
30
+ # ─── Telegram (Optional) ─────────────────────────────────────
31
+ # Bot token from @BotFather
32
+ TELEGRAM_BOT_TOKEN=
33
+ # Secret for validating Telegram webhooks: openssl rand -hex 32
34
+ TELEGRAM_WEBHOOK_SECRET=
35
+ # Verification code for getting your chat ID
36
+ TELEGRAM_VERIFICATION=
37
+ # Default Telegram chat ID for scheduled notifications
38
+ TELEGRAM_CHAT_ID=
39
+
40
+ # ─── LLM Provider ────────────────────────────────────────────
41
+ # Choose your AI provider. Options:
42
+ # anthropic — Claude (Anthropic) [default]
43
+ # openai — GPT (OpenAI)
44
+ # google — Gemini (Google)
45
+ # pragatigpt — PragatiGPT (Gignaati — India-first, 100% data privacy)
46
+ # ollama — Ollama (Local — zero cloud dependency)
47
+ # custom — Any OpenAI-compatible API endpoint
48
+ LLM_PROVIDER=anthropic
49
+
50
+ # Model name override (optional — uses provider default if not set)
51
+ # LLM_MODEL=
52
+
53
+ # Max tokens for LLM responses (default: 4096)
54
+ # LLM_MAX_TOKENS=4096
55
+
56
+ # ─── Anthropic (Claude) ──────────────────────────────────────
57
+ # Required when LLM_PROVIDER=anthropic
58
+ # Get your key at: https://platform.claude.com/settings/keys
59
+ ANTHROPIC_API_KEY=sk-ant-your_key_here
60
+
61
+ # ─── OpenAI (GPT) ────────────────────────────────────────────
62
+ # Required when LLM_PROVIDER=openai
63
+ # Also used for Whisper voice transcription (optional)
64
+ # Get your key at: https://platform.openai.com/settings/organization/api-keys
65
+ OPENAI_API_KEY=
66
+
67
+ # Optional: Override OpenAI base URL for custom endpoints
68
+ # OPENAI_BASE_URL=
69
+
70
+ # ─── Google (Gemini) ─────────────────────────────────────────
71
+ # Required when LLM_PROVIDER=google
72
+ # Get your key at: https://aistudio.google.com/apikey
73
+ GOOGLE_API_KEY=
74
+
75
+ # ─── PragatiGPT (Gignaati — India-first) ─────────────────────
76
+ # Required when LLM_PROVIDER=pragatigpt
77
+ # India's indigenous SLM — 100% data privacy, no foreign cloud dependency
78
+ # Get your key at: https://www.gignaati.com/pragatigpt
79
+ PRAGATIGPT_API_KEY=
80
+ # Optional: Override PragatiGPT base URL (for on-premise deployment)
81
+ # PRAGATIGPT_BASE_URL=https://api.pragatigpt.in/v1
82
+
83
+ # ─── Ollama (Local — Zero Cloud) ─────────────────────────────
84
+ # Required when LLM_PROVIDER=ollama
85
+ # Install Ollama at: https://ollama.com
86
+ # Then pull a model: ollama pull llama3.2
87
+ # Optional: Override Ollama server URL (default: http://localhost:11434)
88
+ # OLLAMA_BASE_URL=http://localhost:11434
89
+
90
+ # ─── Custom OpenAI-Compatible API ────────────────────────────
91
+ # Required when LLM_PROVIDER=custom
92
+ # OPENAI_BASE_URL=http://localhost:11434/v1
93
+ # CUSTOM_API_KEY=your_api_key_here
94
+
95
+ # ─── Docker & Deployment ─────────────────────────────────────
96
+ # Let's Encrypt email for automatic SSL (docker-compose only, optional)
97
+ # LETSENCRYPT_EMAIL=
98
+
99
+ # Giga Bot version — auto-set by postinstall, used by docker-compose for image tags
100
+ # GIGABOT_VERSION=
101
+
102
+ # Custom Docker images (optional, overrides the default gignaati/gigabot images)
103
+ # EVENT_HANDLER_IMAGE_URL=
104
+ # JOB_IMAGE_URL=
@@ -0,0 +1,117 @@
1
+ name: Auto-Merge Job PR
2
+
3
+ on:
4
+ pull_request:
5
+ types: [opened]
6
+ branches: [main]
7
+
8
+ jobs:
9
+ auto-merge:
10
+ runs-on: ${{ vars.RUNS_ON || 'ubuntu-latest' }}
11
+ if: startsWith(github.event.pull_request.head.ref, 'job/')
12
+ permissions:
13
+ contents: write
14
+ pull-requests: write
15
+
16
+ steps:
17
+ - name: Wait for merge check
18
+ id: merge-check
19
+ env:
20
+ GH_TOKEN: ${{ github.token }}
21
+ run: |
22
+ PR_NUMBER="${{ github.event.pull_request.number }}"
23
+ for i in $(seq 1 30); do
24
+ sleep 10
25
+ MERGEABLE=$(gh pr view "$PR_NUMBER" --repo "${{ github.repository }}" --json mergeable -q '.mergeable')
26
+ if [ "$MERGEABLE" != "" ] && [ "$MERGEABLE" != "UNKNOWN" ]; then
27
+ echo "mergeable=$MERGEABLE" >> "$GITHUB_OUTPUT"
28
+ echo "Mergeable: $MERGEABLE"
29
+ exit 0
30
+ fi
31
+ echo "Still computing... (attempt $i/30)"
32
+ done
33
+ echo "mergeable=UNKNOWN" >> "$GITHUB_OUTPUT"
34
+ echo "Timed out waiting for merge check"
35
+
36
+ - name: Check AUTO_MERGE setting
37
+ if: steps.merge-check.outputs.mergeable == 'MERGEABLE'
38
+ id: check-setting
39
+ run: |
40
+ AUTO_MERGE="${{ vars.AUTO_MERGE }}"
41
+ if [ "$AUTO_MERGE" = "false" ]; then
42
+ echo "Auto-merge disabled via AUTO_MERGE=false"
43
+ echo "enabled=false" >> "$GITHUB_OUTPUT"
44
+ else
45
+ echo "Auto-merge enabled"
46
+ echo "enabled=true" >> "$GITHUB_OUTPUT"
47
+ fi
48
+
49
+ - name: Check ALLOWED_PATHS
50
+ if: steps.merge-check.outputs.mergeable == 'MERGEABLE' && steps.check-setting.outputs.enabled == 'true'
51
+ id: check-paths
52
+ env:
53
+ GH_TOKEN: ${{ github.token }}
54
+ run: |
55
+ ALLOWED_PATHS="${{ vars.ALLOWED_PATHS }}"
56
+ PR_NUMBER="${{ github.event.pull_request.number }}"
57
+
58
+ # Default: only logs allowed
59
+ if [ -z "$ALLOWED_PATHS" ]; then
60
+ ALLOWED_PATHS="/logs"
61
+ fi
62
+
63
+ # Normalize: ensure each prefix starts with /
64
+ IFS=',' read -ra RAW_PREFIXES <<< "$ALLOWED_PATHS"
65
+ PREFIXES=()
66
+ for p in "${RAW_PREFIXES[@]}"; do
67
+ trimmed=$(echo "$p" | xargs)
68
+ [ -z "$trimmed" ] && continue
69
+ # Ensure leading /
70
+ [[ "$trimmed" != /* ]] && trimmed="/$trimmed"
71
+ PREFIXES+=("$trimmed")
72
+ done
73
+
74
+ # "/" means everything allowed
75
+ for p in "${PREFIXES[@]}"; do
76
+ if [ "$p" = "/" ]; then
77
+ echo "All paths allowed (ALLOWED_PATHS contains /)"
78
+ echo "allowed=true" >> "$GITHUB_OUTPUT"
79
+ exit 0
80
+ fi
81
+ done
82
+
83
+ # Get changed files
84
+ CHANGED_FILES=$(gh pr diff "$PR_NUMBER" --name-only --repo "${{ github.repository }}")
85
+ if [ -z "$CHANGED_FILES" ]; then
86
+ echo "No changed files"
87
+ echo "allowed=true" >> "$GITHUB_OUTPUT"
88
+ exit 0
89
+ fi
90
+
91
+ # Check each file against prefixes
92
+ # Strip leading / from prefixes for comparison (git paths are relative)
93
+ ALL_OK=true
94
+ while IFS= read -r file; do
95
+ [ -z "$file" ] && continue
96
+ FILE_OK=false
97
+ for prefix in "${PREFIXES[@]}"; do
98
+ compare="${prefix#/}"
99
+ if [[ "$file" == "$compare"* ]]; then
100
+ FILE_OK=true
101
+ break
102
+ fi
103
+ done
104
+ if [ "$FILE_OK" = "false" ]; then
105
+ echo "BLOCKED: $file"
106
+ ALL_OK=false
107
+ fi
108
+ done <<< "$CHANGED_FILES"
109
+
110
+ echo "allowed=$ALL_OK" >> "$GITHUB_OUTPUT"
111
+
112
+ - name: Merge PR
113
+ if: steps.merge-check.outputs.mergeable == 'MERGEABLE' && steps.check-setting.outputs.enabled == 'true' && steps.check-paths.outputs.allowed == 'true'
114
+ env:
115
+ GH_TOKEN: ${{ github.token }}
116
+ run: |
117
+ gh pr merge ${{ github.event.pull_request.number }} --squash --delete-branch --repo "${{ github.repository }}"
@@ -0,0 +1,64 @@
1
+ name: Notify Job Failed
2
+
3
+ on:
4
+ workflow_run:
5
+ workflows: ["Agent Job"]
6
+ types: [completed]
7
+
8
+ jobs:
9
+ notify-failure:
10
+ runs-on: ${{ vars.RUNS_ON || 'ubuntu-latest' }}
11
+ if: >
12
+ github.event.workflow_run.conclusion != 'success' &&
13
+ startsWith(github.event.workflow_run.head_branch, 'job/')
14
+ permissions:
15
+ contents: read
16
+
17
+ steps:
18
+ - name: Checkout job branch
19
+ uses: actions/checkout@v4
20
+ with:
21
+ ref: ${{ github.event.workflow_run.head_branch }}
22
+
23
+ - name: Send failure notification
24
+ env:
25
+ GH_TOKEN: ${{ github.token }}
26
+ run: |
27
+ BRANCH="${{ github.event.workflow_run.head_branch }}"
28
+ JOB_ID="${BRANCH#job/}"
29
+ RUN_ID="${{ github.event.workflow_run.id }}"
30
+ RUN_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${RUN_ID}"
31
+
32
+ # Read job description from job.config.json (exists on branch from job creation)
33
+ JOB_CONTENT=""
34
+ if [ -f "logs/${JOB_ID}/job.config.json" ]; then
35
+ JOB_CONTENT=$(jq -r '.job // empty' "logs/${JOB_ID}/job.config.json")
36
+ fi
37
+
38
+ COMMIT_SHA=$(git rev-parse HEAD)
39
+ STATUS="${{ github.event.workflow_run.conclusion }}"
40
+
41
+ jq -n \
42
+ --arg job_id "$JOB_ID" \
43
+ --arg branch "$BRANCH" \
44
+ --arg status "$STATUS" \
45
+ --arg job "$JOB_CONTENT" \
46
+ --arg run_url "$RUN_URL" \
47
+ --arg commit_sha "$COMMIT_SHA" \
48
+ '{
49
+ job_id: $job_id,
50
+ branch: $branch,
51
+ status: $status,
52
+ job: $job,
53
+ run_url: $run_url,
54
+ pr_url: "",
55
+ changed_files: [],
56
+ commit_message: "",
57
+ commit_sha: $commit_sha,
58
+ merge_result: ""
59
+ }' > /tmp/payload.json
60
+
61
+ curl -s -X POST "${{ vars.APP_URL }}/api/github/webhook" \
62
+ -H "Content-Type: application/json" \
63
+ -H "X-GitHub-Webhook-Secret-Token: ${{ secrets.GH_WEBHOOK_SECRET }}" \
64
+ -d @/tmp/payload.json
@@ -0,0 +1,119 @@
1
+ name: Notify Event Handler
2
+
3
+ on:
4
+ workflow_run:
5
+ workflows: ["Auto-Merge Job PR"]
6
+ types: [completed]
7
+
8
+ jobs:
9
+ notify:
10
+ runs-on: ${{ vars.RUNS_ON || 'ubuntu-latest' }}
11
+ if: startsWith(github.event.workflow_run.head_branch, 'job/')
12
+ permissions:
13
+ contents: read
14
+ pull-requests: read
15
+
16
+ steps:
17
+ - name: Get PR number
18
+ id: pr
19
+ env:
20
+ GH_TOKEN: ${{ github.token }}
21
+ run: |
22
+ BRANCH="${{ github.event.workflow_run.head_branch }}"
23
+
24
+ # Try workflow_run.pull_requests first
25
+ PR_NUMBER=$(echo '${{ toJson(github.event.workflow_run.pull_requests) }}' | jq -r '.[0].number // empty')
26
+
27
+ # Fallback: look up PR by head branch
28
+ if [ -z "$PR_NUMBER" ]; then
29
+ PR_NUMBER=$(gh pr list --head "$BRANCH" --state all --repo "${{ github.repository }}" --json number -q '.[0].number')
30
+ fi
31
+
32
+ if [ -z "$PR_NUMBER" ]; then
33
+ echo "No PR found for branch $BRANCH"
34
+ exit 1
35
+ fi
36
+
37
+ PR_URL=$(gh pr view "$PR_NUMBER" --repo "${{ github.repository }}" --json url -q '.url')
38
+
39
+ echo "number=$PR_NUMBER" >> "$GITHUB_OUTPUT"
40
+ echo "url=$PR_URL" >> "$GITHUB_OUTPUT"
41
+ echo "Found PR #$PR_NUMBER"
42
+
43
+ - name: Checkout PR branch
44
+ uses: actions/checkout@v4
45
+ with:
46
+ ref: ${{ github.event.workflow_run.head_sha }}
47
+
48
+ - name: Gather job results and notify
49
+ env:
50
+ GH_TOKEN: ${{ github.token }}
51
+ run: |
52
+ PR_NUMBER="${{ steps.pr.outputs.number }}"
53
+ BRANCH="${{ github.event.workflow_run.head_branch }}"
54
+ JOB_ID="${BRANCH#job/}"
55
+ RUN_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }}"
56
+
57
+ # Check actual PR merge state
58
+ PR_MERGED=$(gh pr view "$PR_NUMBER" --repo "${{ github.repository }}" --json mergedAt -q '.mergedAt')
59
+ if [ -n "$PR_MERGED" ] && [ "$PR_MERGED" != "null" ]; then
60
+ MERGE_RESULT="merged"
61
+ else
62
+ MERGE_RESULT="not_merged"
63
+ fi
64
+
65
+ # 1. Read job description from job.config.json (on disk after checkout)
66
+ JOB_CONTENT=""
67
+ if [ -f "logs/${JOB_ID}/job.config.json" ]; then
68
+ JOB_CONTENT=$(jq -r '.job // empty' "logs/${JOB_ID}/job.config.json")
69
+ fi
70
+
71
+ # 2. Get last commit message via gh CLI
72
+ COMMIT_MSG=$(gh pr view "$PR_NUMBER" --json commits --jq '.commits[-1].messageHeadline' --repo "${{ github.repository }}" 2>/dev/null || echo "")
73
+
74
+ # 3. Get changed files (names only)
75
+ CHANGED_FILES=$(gh pr diff "$PR_NUMBER" --name-only --repo "${{ github.repository }}" 2>/dev/null || echo "")
76
+
77
+ # 4. Get PR status
78
+ PR_STATUS=$(gh pr view "$PR_NUMBER" --json state --jq '.state' --repo "${{ github.repository }}" 2>/dev/null || echo "open")
79
+
80
+ # 5. Commit SHA for server-side log fetching
81
+ COMMIT_SHA="${{ github.event.workflow_run.head_sha }}"
82
+
83
+ # Determine status based on merge result and PR state
84
+ if [ "$MERGE_RESULT" = "merged" ]; then
85
+ STATUS="completed"
86
+ else
87
+ STATUS="$PR_STATUS"
88
+ fi
89
+
90
+ # 6. Build flat JSON payload
91
+ jq -n \
92
+ --arg job_id "$JOB_ID" \
93
+ --arg branch "$BRANCH" \
94
+ --arg status "$STATUS" \
95
+ --arg job "$JOB_CONTENT" \
96
+ --arg run_url "$RUN_URL" \
97
+ --arg pr_url "${{ steps.pr.outputs.url }}" \
98
+ --arg commit_message "$COMMIT_MSG" \
99
+ --arg changed_files "$CHANGED_FILES" \
100
+ --arg commit_sha "$COMMIT_SHA" \
101
+ --arg merge_result "$MERGE_RESULT" \
102
+ '{
103
+ job_id: $job_id,
104
+ branch: $branch,
105
+ status: $status,
106
+ job: $job,
107
+ run_url: $run_url,
108
+ pr_url: $pr_url,
109
+ changed_files: ($changed_files | split("\n") | map(select(length > 0))),
110
+ commit_message: $commit_message,
111
+ commit_sha: $commit_sha,
112
+ merge_result: $merge_result
113
+ }' > /tmp/payload.json
114
+
115
+ # 7. Send to event handler
116
+ curl -s -X POST "${{ vars.APP_URL }}/api/github/webhook" \
117
+ -H "Content-Type: application/json" \
118
+ -H "X-GitHub-Webhook-Secret-Token: ${{ secrets.GH_WEBHOOK_SECRET }}" \
119
+ -d @/tmp/payload.json
@@ -0,0 +1,121 @@
1
+ name: Rebuild Event Handler
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+
7
+ concurrency:
8
+ group: event-handler-deploy
9
+ cancel-in-progress: false
10
+
11
+ jobs:
12
+ deploy:
13
+ runs-on: self-hosted
14
+ timeout-minutes: 20
15
+ steps:
16
+ - name: Pull latest and detect version change
17
+ id: pull
18
+ run: |
19
+ docker exec gigaclaw-event-handler bash -c '
20
+ export GH_TOKEN=$(grep "^GH_TOKEN=" /app/.env | cut -d= -f2-)
21
+ echo "${GH_TOKEN}" | gh auth login --with-token
22
+ gh auth setup-git
23
+
24
+ git fetch origin main
25
+ CHANGED=$(git diff --name-only HEAD origin/main)
26
+ if ! echo "$CHANGED" | grep -qv "^logs/"; then
27
+ echo "SKIP" > /app/.rebuild-status
28
+ git reset --hard origin/main
29
+ exit 0
30
+ fi
31
+
32
+ # Detect gigaclaw version change from package-lock.json in git
33
+ git show HEAD:package-lock.json > /tmp/old-lock.json 2>/dev/null || echo "{}" > /tmp/old-lock.json
34
+ git show origin/main:package-lock.json > /tmp/new-lock.json
35
+ OLD_TPB=$(node -p "try{require(\"/tmp/old-lock.json\").packages[\"node_modules/gigaclaw\"]?.version||\"\"}catch(e){\"\"}")
36
+ NEW_TPB=$(node -p "try{require(\"/tmp/new-lock.json\").packages[\"node_modules/gigaclaw\"]?.version||\"\"}catch(e){\"\"}")
37
+ rm -f /tmp/old-lock.json /tmp/new-lock.json
38
+
39
+ git reset --hard origin/main
40
+
41
+ if [ -n "$OLD_TPB" ] && [ "$OLD_TPB" != "$NEW_TPB" ]; then
42
+ # Version changed — run gigaclaw init to scaffold new templates
43
+ if echo "$NEW_TPB" | grep -q "-"; then
44
+ npx --yes "gigaclaw@${NEW_TPB}" init
45
+ else
46
+ npx --yes gigaclaw@latest init
47
+ fi
48
+
49
+ # Commit any template changes from init
50
+ git add -A
51
+ if ! git diff --cached --quiet; then
52
+ git commit -m "chore: apply gigaclaw init after upgrade"
53
+ git push origin main
54
+ fi
55
+
56
+ # Update GIGACLAW_VERSION in .env so docker compose pulls the right image
57
+ if grep -q "^GIGACLAW_VERSION=" /app/.env; then
58
+ sed -i "s/^GIGACLAW_VERSION=.*/GIGACLAW_VERSION=$NEW_TPB/" /app/.env
59
+ else
60
+ echo "GIGACLAW_VERSION=$NEW_TPB" >> /app/.env
61
+ fi
62
+ echo "VERSION_CHANGED" > /app/.rebuild-status
63
+ else
64
+ npm install --omit=dev
65
+ echo "REBUILD" > /app/.rebuild-status
66
+ fi
67
+ '
68
+
69
+ STATUS=$(docker exec gigaclaw-event-handler cat /app/.rebuild-status)
70
+ docker exec gigaclaw-event-handler rm -f /app/.rebuild-status
71
+ echo "status=$STATUS" >> $GITHUB_OUTPUT
72
+
73
+ - name: Rebuild (no version change)
74
+ if: steps.pull.outputs.status == 'REBUILD'
75
+ run: |
76
+ docker exec gigaclaw-event-handler bash -c '
77
+ rm -rf .next-new .next-old
78
+ NEXT_BUILD_DIR=.next-new npm run build
79
+
80
+ mv .next .next-old 2>/dev/null || true
81
+ mv .next-new .next
82
+
83
+ echo "Rebuild complete, reloading Next.js..."
84
+ npx pm2 reload all
85
+
86
+ rm -rf .next-old
87
+ '
88
+
89
+ - name: Pull new image and restart container
90
+ if: steps.pull.outputs.status == 'VERSION_CHANGED'
91
+ run: |
92
+ HOST_DIR=$(docker inspect gigaclaw-event-handler --format '{{index .Config.Labels "com.docker.compose.project.working_dir"}}')
93
+ docker compose -f /project/docker-compose.yml --project-directory "$HOST_DIR" --env-file /project/.env pull event-handler
94
+ docker stop gigaclaw-event-handler || true
95
+ docker rm gigaclaw-event-handler || true
96
+ docker compose -f /project/docker-compose.yml --project-directory "$HOST_DIR" --env-file /project/.env up -d event-handler
97
+
98
+ - name: Rebuild in new container
99
+ if: steps.pull.outputs.status == 'VERSION_CHANGED'
100
+ run: |
101
+ echo "Waiting for new container..."
102
+ for i in $(seq 1 30); do
103
+ if docker exec gigaclaw-event-handler echo "ready" 2>/dev/null; then
104
+ break
105
+ fi
106
+ sleep 2
107
+ done
108
+
109
+ docker exec gigaclaw-event-handler bash -c '
110
+ npm install --omit=dev
111
+
112
+ rm -rf .next-new .next-old
113
+ NEXT_BUILD_DIR=.next-new npm run build
114
+
115
+ mv .next .next-old 2>/dev/null || true
116
+ mv .next-new .next
117
+
118
+ npx pm2 restart all
119
+
120
+ rm -rf .next-old
121
+ '