thepopebot 1.2.47 → 1.2.49

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.
@@ -264,35 +264,3 @@ export async function getSwarmConfig() {
264
264
  return { crons, triggers };
265
265
  }
266
266
 
267
- /**
268
- * Cancel a running swarm job.
269
- * @param {number} runId - Workflow run ID
270
- * @returns {Promise<object>}
271
- */
272
- export async function cancelSwarmJob(runId) {
273
- await requireAuth();
274
- try {
275
- const { cancelWorkflowRun } = await import('../tools/github.js');
276
- return await cancelWorkflowRun(runId);
277
- } catch (err) {
278
- console.error('Failed to cancel workflow run:', err);
279
- return { error: err.message || 'Failed to cancel workflow run' };
280
- }
281
- }
282
-
283
- /**
284
- * Rerun a swarm job (all or failed only).
285
- * @param {number} runId - Workflow run ID
286
- * @param {boolean} [failedOnly=false]
287
- * @returns {Promise<object>}
288
- */
289
- export async function rerunSwarmJob(runId, failedOnly = false) {
290
- await requireAuth();
291
- try {
292
- const { rerunWorkflowRun } = await import('../tools/github.js');
293
- return await rerunWorkflowRun(runId, !!failedOnly);
294
- } catch (err) {
295
- console.error('Failed to rerun workflow:', err);
296
- return { error: err.message || 'Failed to rerun workflow' };
297
- }
298
- }
@@ -2,8 +2,8 @@
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useState, useEffect, useCallback } from "react";
4
4
  import { PageLayout } from "./page-layout.js";
5
- import { StopIcon, SpinnerIcon, RefreshIcon } from "./icons.js";
6
- import { getSwarmStatus, cancelSwarmJob, rerunSwarmJob } from "../actions.js";
5
+ import { SpinnerIcon, RefreshIcon } from "./icons.js";
6
+ import { getSwarmStatus } from "../actions.js";
7
7
  function formatDuration(seconds) {
8
8
  if (seconds < 60) return `${seconds}s`;
9
9
  const minutes = Math.floor(seconds / 60);
@@ -55,7 +55,7 @@ const conclusionBadgeStyles = {
55
55
  cancelled: "bg-yellow-500/10 text-yellow-500",
56
56
  skipped: "bg-muted text-muted-foreground"
57
57
  };
58
- function SwarmWorkflowList({ runs, onCancel, onRerun }) {
58
+ function SwarmWorkflowList({ runs }) {
59
59
  if (!runs || runs.length === 0) {
60
60
  return /* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground py-4 text-center", children: "No workflow runs." });
61
61
  }
@@ -76,43 +76,16 @@ function SwarmWorkflowList({ runs, onCancel, onRerun }) {
76
76
  /* @__PURE__ */ jsx("span", { className: "text-sm font-medium truncate", children: run.workflow_name || run.branch }),
77
77
  /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground shrink-0", children: isActive ? formatDuration(run.duration_seconds) : timeAgo(run.updated_at || run.started_at) }),
78
78
  /* @__PURE__ */ jsx("div", { className: "flex-1" }),
79
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 shrink-0", children: [
80
- run.html_url && /* @__PURE__ */ jsx(
81
- "a",
82
- {
83
- href: run.html_url,
84
- target: "_blank",
85
- rel: "noopener noreferrer",
86
- className: "text-xs text-blue-500 hover:underline",
87
- children: "View"
88
- }
89
- ),
90
- isActive && /* @__PURE__ */ jsx(
91
- "button",
92
- {
93
- onClick: () => onCancel(run.run_id),
94
- className: "inline-flex items-center justify-center rounded-md p-1.5 text-muted-foreground hover:bg-destructive/10 hover:text-destructive",
95
- title: "Cancel",
96
- children: /* @__PURE__ */ jsx(StopIcon, { size: 14 })
97
- }
98
- ),
99
- !isActive && /* @__PURE__ */ jsx(
100
- "button",
101
- {
102
- onClick: () => onRerun(run.run_id, false),
103
- className: "text-xs rounded-md px-2 py-1 border hover:bg-accent",
104
- children: "Rerun"
105
- }
106
- ),
107
- !isActive && run.conclusion === "failure" && /* @__PURE__ */ jsx(
108
- "button",
109
- {
110
- onClick: () => onRerun(run.run_id, true),
111
- className: "text-xs rounded-md px-2 py-1 border text-red-500 hover:bg-red-500/10",
112
- children: "Rerun failed"
113
- }
114
- )
115
- ] })
79
+ run.html_url && /* @__PURE__ */ jsx(
80
+ "a",
81
+ {
82
+ href: run.html_url,
83
+ target: "_blank",
84
+ rel: "noopener noreferrer",
85
+ className: "text-xs text-blue-500 hover:underline shrink-0",
86
+ children: "View"
87
+ }
88
+ )
116
89
  ] }, run.run_id);
117
90
  }) });
118
91
  }
@@ -173,22 +146,6 @@ function SwarmPage({ session }) {
173
146
  }
174
147
  setLoadingMore(false);
175
148
  };
176
- const handleCancel = async (runId) => {
177
- try {
178
- await cancelSwarmJob(runId);
179
- await fetchStatus();
180
- } catch (err) {
181
- console.error("Failed to cancel job:", err);
182
- }
183
- };
184
- const handleRerun = async (runId, failedOnly) => {
185
- try {
186
- await rerunSwarmJob(runId, failedOnly);
187
- await fetchStatus();
188
- } catch (err) {
189
- console.error("Failed to rerun job:", err);
190
- }
191
- };
192
149
  return /* @__PURE__ */ jsxs(PageLayout, { session, children: [
193
150
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-6", children: [
194
151
  /* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold", children: "Swarm" }),
@@ -212,14 +169,7 @@ function SwarmPage({ session }) {
212
169
  /* @__PURE__ */ jsx(SwarmSummaryCards, { counts }),
213
170
  /* @__PURE__ */ jsxs("div", { children: [
214
171
  /* @__PURE__ */ jsx("h2", { className: "text-sm font-medium text-muted-foreground uppercase tracking-wide mb-3", children: "Workflow Runs" }),
215
- /* @__PURE__ */ jsx(
216
- SwarmWorkflowList,
217
- {
218
- runs,
219
- onCancel: handleCancel,
220
- onRerun: handleRerun
221
- }
222
- ),
172
+ /* @__PURE__ */ jsx(SwarmWorkflowList, { runs }),
223
173
  hasMore && /* @__PURE__ */ jsx("div", { className: "flex justify-center mt-4", children: /* @__PURE__ */ jsx(
224
174
  "button",
225
175
  {
@@ -2,8 +2,8 @@
2
2
 
3
3
  import { useState, useEffect, useCallback } from 'react';
4
4
  import { PageLayout } from './page-layout.js';
5
- import { StopIcon, SpinnerIcon, RefreshIcon } from './icons.js';
6
- import { getSwarmStatus, cancelSwarmJob, rerunSwarmJob } from '../actions.js';
5
+ import { SpinnerIcon, RefreshIcon } from './icons.js';
6
+ import { getSwarmStatus } from '../actions.js';
7
7
 
8
8
  // ─────────────────────────────────────────────────────────────────────────────
9
9
  // Utilities
@@ -84,7 +84,7 @@ const conclusionBadgeStyles = {
84
84
  skipped: 'bg-muted text-muted-foreground',
85
85
  };
86
86
 
87
- function SwarmWorkflowList({ runs, onCancel, onRerun }) {
87
+ function SwarmWorkflowList({ runs }) {
88
88
  if (!runs || runs.length === 0) {
89
89
  return (
90
90
  <div className="text-sm text-muted-foreground py-4 text-center">
@@ -134,44 +134,17 @@ function SwarmWorkflowList({ runs, onCancel, onRerun }) {
134
134
  {/* Spacer */}
135
135
  <div className="flex-1" />
136
136
 
137
- {/* Actions */}
138
- <div className="flex items-center gap-2 shrink-0">
139
- {run.html_url && (
140
- <a
141
- href={run.html_url}
142
- target="_blank"
143
- rel="noopener noreferrer"
144
- className="text-xs text-blue-500 hover:underline"
145
- >
146
- View
147
- </a>
148
- )}
149
- {isActive && (
150
- <button
151
- onClick={() => onCancel(run.run_id)}
152
- className="inline-flex items-center justify-center rounded-md p-1.5 text-muted-foreground hover:bg-destructive/10 hover:text-destructive"
153
- title="Cancel"
154
- >
155
- <StopIcon size={14} />
156
- </button>
157
- )}
158
- {!isActive && (
159
- <button
160
- onClick={() => onRerun(run.run_id, false)}
161
- className="text-xs rounded-md px-2 py-1 border hover:bg-accent"
162
- >
163
- Rerun
164
- </button>
165
- )}
166
- {!isActive && run.conclusion === 'failure' && (
167
- <button
168
- onClick={() => onRerun(run.run_id, true)}
169
- className="text-xs rounded-md px-2 py-1 border text-red-500 hover:bg-red-500/10"
170
- >
171
- Rerun failed
172
- </button>
173
- )}
174
- </div>
137
+ {/* Link */}
138
+ {run.html_url && (
139
+ <a
140
+ href={run.html_url}
141
+ target="_blank"
142
+ rel="noopener noreferrer"
143
+ className="text-xs text-blue-500 hover:underline shrink-0"
144
+ >
145
+ View
146
+ </a>
147
+ )}
175
148
  </div>
176
149
  );
177
150
  })}
@@ -246,24 +219,6 @@ export function SwarmPage({ session }) {
246
219
  setLoadingMore(false);
247
220
  };
248
221
 
249
- const handleCancel = async (runId) => {
250
- try {
251
- await cancelSwarmJob(runId);
252
- await fetchStatus();
253
- } catch (err) {
254
- console.error('Failed to cancel job:', err);
255
- }
256
- };
257
-
258
- const handleRerun = async (runId, failedOnly) => {
259
- try {
260
- await rerunSwarmJob(runId, failedOnly);
261
- await fetchStatus();
262
- } catch (err) {
263
- console.error('Failed to rerun job:', err);
264
- }
265
- };
266
-
267
222
  return (
268
223
  <PageLayout session={session}>
269
224
  {/* Header */}
@@ -302,11 +257,7 @@ export function SwarmPage({ session }) {
302
257
  <h2 className="text-sm font-medium text-muted-foreground uppercase tracking-wide mb-3">
303
258
  Workflow Runs
304
259
  </h2>
305
- <SwarmWorkflowList
306
- runs={runs}
307
- onCancel={handleCancel}
308
- onRerun={handleRerun}
309
- />
260
+ <SwarmWorkflowList runs={runs} />
310
261
  {hasMore && (
311
262
  <div className="flex justify-center mt-4">
312
263
  <button
@@ -158,55 +158,6 @@ async function getSwarmStatus(page = 1) {
158
158
  };
159
159
  }
160
160
 
161
- /**
162
- * Cancel a workflow run
163
- * @param {number} runId - Workflow run ID
164
- */
165
- async function cancelWorkflowRun(runId) {
166
- const { GH_OWNER, GH_REPO } = process.env;
167
- const res = await fetch(
168
- `https://api.github.com/repos/${GH_OWNER}/${GH_REPO}/actions/runs/${runId}/cancel`,
169
- {
170
- method: 'POST',
171
- headers: {
172
- 'Authorization': `Bearer ${process.env.GH_TOKEN}`,
173
- 'Accept': 'application/vnd.github+json',
174
- 'X-GitHub-Api-Version': '2022-11-28',
175
- },
176
- }
177
- );
178
- if (!res.ok && res.status !== 202) {
179
- const error = await res.text();
180
- throw new Error(`GitHub API error: ${res.status} ${error}`);
181
- }
182
- return { success: true };
183
- }
184
-
185
- /**
186
- * Re-run a workflow run (all jobs or failed only)
187
- * @param {number} runId - Workflow run ID
188
- * @param {boolean} [failedOnly=false] - Only rerun failed jobs
189
- */
190
- async function rerunWorkflowRun(runId, failedOnly = false) {
191
- const { GH_OWNER, GH_REPO } = process.env;
192
- const endpoint = failedOnly
193
- ? `https://api.github.com/repos/${GH_OWNER}/${GH_REPO}/actions/runs/${runId}/rerun-failed-jobs`
194
- : `https://api.github.com/repos/${GH_OWNER}/${GH_REPO}/actions/runs/${runId}/rerun`;
195
- const res = await fetch(endpoint, {
196
- method: 'POST',
197
- headers: {
198
- 'Authorization': `Bearer ${process.env.GH_TOKEN}`,
199
- 'Accept': 'application/vnd.github+json',
200
- 'X-GitHub-Api-Version': '2022-11-28',
201
- },
202
- });
203
- if (!res.ok && res.status !== 201) {
204
- const error = await res.text();
205
- throw new Error(`GitHub API error: ${res.status} ${error}`);
206
- }
207
- return { success: true };
208
- }
209
-
210
161
  /**
211
162
  * Trigger a workflow via workflow_dispatch
212
163
  * @param {string} workflowId - Workflow file name (e.g., 'upgrade-event-handler.yml')
@@ -241,7 +192,5 @@ export {
241
192
  getWorkflowRunJobs,
242
193
  getJobStatus,
243
194
  getSwarmStatus,
244
- cancelWorkflowRun,
245
- rerunWorkflowRun,
246
195
  triggerWorkflowDispatch,
247
196
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thepopebot",
3
- "version": "1.2.47",
3
+ "version": "1.2.49",
4
4
  "type": "module",
5
5
  "description": "Create autonomous AI agents with a two-layer architecture: Next.js Event Handler + Docker Agent.",
6
6
  "bin": {
@@ -12,38 +12,104 @@ jobs:
12
12
  deploy:
13
13
  runs-on: self-hosted
14
14
  steps:
15
- - name: Deploy event handler
15
+ - name: Pull latest and detect version change
16
+ id: pull
16
17
  run: |
17
18
  docker exec thepopebot-event-handler bash -c '
18
- # Load GH_TOKEN and authenticate git via GitHub CLI
19
19
  export GH_TOKEN=$(grep "^GH_TOKEN=" /app/.env | cut -d= -f2-)
20
20
  echo "${GH_TOKEN}" | gh auth login --with-token
21
21
  gh auth setup-git
22
22
 
23
- # Check if there are code changes (not just logs)
24
23
  git fetch origin main
25
24
  CHANGED=$(git diff --name-only HEAD origin/main)
26
25
  if ! echo "$CHANGED" | grep -qv "^logs/"; then
27
- echo "Logs-only change, skipping rebuild"
26
+ echo "SKIP" > /app/.rebuild-status
28
27
  git reset --hard origin/main
29
28
  exit 0
30
29
  fi
31
30
 
32
- # Pull latest code and install dependencies
31
+ # Detect thepopebot version change from package-lock.json in git
32
+ git show HEAD:package-lock.json > /tmp/old-lock.json 2>/dev/null || echo "{}" > /tmp/old-lock.json
33
+ git show origin/main:package-lock.json > /tmp/new-lock.json
34
+ OLD_TPB=$(node -p "try{require(\"/tmp/old-lock.json\").packages[\"node_modules/thepopebot\"]?.version||\"\"}catch(e){\"\"}")
35
+ NEW_TPB=$(node -p "try{require(\"/tmp/new-lock.json\").packages[\"node_modules/thepopebot\"]?.version||\"\"}catch(e){\"\"}")
36
+ rm -f /tmp/old-lock.json /tmp/new-lock.json
37
+
33
38
  git reset --hard origin/main
34
- npm install --omit=dev
35
39
 
36
- # Clean build to a separate directory (no stale cache)
40
+ if [ -n "$OLD_TPB" ] && [ "$OLD_TPB" != "$NEW_TPB" ]; then
41
+ # Version changed — run thepopebot init to scaffold new templates
42
+ npx thepopebot init
43
+ npm install --omit=dev
44
+
45
+ # Commit any template changes from init
46
+ git add -A
47
+ if ! git diff --cached --quiet; then
48
+ git commit -m "chore: apply thepopebot init after upgrade"
49
+ git push origin main
50
+ fi
51
+
52
+ # Update THEPOPEBOT_VERSION in .env so docker compose pulls the right image
53
+ if grep -q "^THEPOPEBOT_VERSION=" /app/.env; then
54
+ sed -i "s/^THEPOPEBOT_VERSION=.*/THEPOPEBOT_VERSION=$NEW_TPB/" /app/.env
55
+ else
56
+ echo "THEPOPEBOT_VERSION=$NEW_TPB" >> /app/.env
57
+ fi
58
+ echo "VERSION_CHANGED" > /app/.rebuild-status
59
+ else
60
+ npm install --omit=dev
61
+ echo "REBUILD" > /app/.rebuild-status
62
+ fi
63
+ '
64
+
65
+ STATUS=$(docker exec thepopebot-event-handler cat /app/.rebuild-status)
66
+ docker exec thepopebot-event-handler rm -f /app/.rebuild-status
67
+ echo "status=$STATUS" >> $GITHUB_OUTPUT
68
+
69
+ - name: Rebuild (no version change)
70
+ if: steps.pull.outputs.status == 'REBUILD'
71
+ run: |
72
+ docker exec thepopebot-event-handler bash -c '
37
73
  rm -rf .next-new .next-old
38
74
  NEXT_BUILD_DIR=.next-new npm run build
39
75
 
40
- # Atomic swap: old .next keeps serving until pm2 reload
41
76
  mv .next .next-old 2>/dev/null || true
42
77
  mv .next-new .next
43
78
 
44
79
  echo "Rebuild complete, reloading Next.js..."
45
80
  npx pm2 reload all
46
81
 
47
- # Cleanup
82
+ rm -rf .next-old
83
+ '
84
+
85
+ - name: Pull new image and restart container
86
+ if: steps.pull.outputs.status == 'VERSION_CHANGED'
87
+ run: |
88
+ cd /project
89
+ docker compose pull event-handler
90
+ docker compose up -d event-handler
91
+
92
+ - name: Rebuild in new container
93
+ if: steps.pull.outputs.status == 'VERSION_CHANGED'
94
+ run: |
95
+ echo "Waiting for new container..."
96
+ for i in $(seq 1 30); do
97
+ if docker exec thepopebot-event-handler echo "ready" 2>/dev/null; then
98
+ break
99
+ fi
100
+ sleep 2
101
+ done
102
+
103
+ docker exec thepopebot-event-handler bash -c '
104
+ npm install --omit=dev
105
+
106
+ rm -rf .next-new .next-old
107
+ NEXT_BUILD_DIR=.next-new npm run build
108
+
109
+ mv .next .next-old 2>/dev/null || true
110
+ mv .next-new .next
111
+
112
+ npx pm2 reload all
113
+
48
114
  rm -rf .next-old
49
115
  '
@@ -13,32 +13,31 @@ jobs:
13
13
  steps:
14
14
  - name: Upgrade thepopebot
15
15
  run: |
16
- # Phase 1: Update package and build (but don't swap yet)
17
16
  docker exec thepopebot-event-handler bash -c '
18
17
  export GH_TOKEN=$(grep "^GH_TOKEN=" /app/.env | cut -d= -f2-)
19
18
  echo "${GH_TOKEN}" | gh auth login --with-token
20
19
  gh auth setup-git
21
20
 
22
- git fetch origin main
23
- git reset --hard origin/main
21
+ REPO_URL=$(git -C /app remote get-url origin)
22
+ WORK_DIR=$(mktemp -d)
23
+ git clone --depth 1 "$REPO_URL" "$WORK_DIR"
24
+ cd "$WORK_DIR"
24
25
 
26
+ npm install
25
27
  npm update thepopebot
26
- npx thepopebot init
27
- npm install --omit=dev
28
28
 
29
- rm -rf .next-new
30
- NEXT_BUILD_DIR=.next-new npm run build
31
- '
32
-
33
- # Phase 2: Pull new image and restart container
34
- cd /project
35
- docker compose pull event-handler
36
- docker compose up -d event-handler
37
-
38
- # Phase 3: Swap build and reload inside new container
39
- docker exec thepopebot-event-handler bash -c '
40
- mv .next .next-old 2>/dev/null || true
41
- mv .next-new .next
42
- npx pm2 reload all
43
- rm -rf .next-old
29
+ git add -A
30
+ if ! git diff --cached --quiet; then
31
+ VERSION=$(node -p "require('./node_modules/thepopebot/package.json').version")
32
+ BRANCH="upgrade/thepopebot-${VERSION}-$(date +%s)"
33
+ git checkout -b "$BRANCH"
34
+ git commit -m "chore: upgrade thepopebot"
35
+ git push origin "$BRANCH"
36
+ gh pr create --title "chore: upgrade thepopebot" --body "Automated upgrade via upgrade-event-handler workflow." --base main --head "$BRANCH"
37
+ gh pr merge "$BRANCH" --squash --auto --delete-branch
38
+ else
39
+ echo "No changes — thepopebot is already up to date."
40
+ fi
41
+
42
+ rm -rf "$WORK_DIR"
44
43
  '