bosun 0.32.0 → 0.33.1

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 (64) hide show
  1. package/.env.example +11 -5
  2. package/README.md +3 -0
  3. package/agent-event-bus.mjs +1 -0
  4. package/agent-prompts.mjs +104 -2
  5. package/anomaly-detector.mjs +54 -0
  6. package/codex-config.mjs +104 -0
  7. package/github-app-auth.mjs +7 -7
  8. package/github-auth-manager.mjs +2 -2
  9. package/github-oauth-portal.mjs +7 -7
  10. package/kanban-adapter.mjs +258 -3
  11. package/monitor.mjs +6 -3
  12. package/package.json +14 -4
  13. package/postinstall.mjs +17 -0
  14. package/primary-agent.mjs +46 -1
  15. package/review-agent.mjs +9 -4
  16. package/session-tracker.mjs +1 -0
  17. package/setup-web-server.mjs +388 -5
  18. package/task-attachments.mjs +187 -0
  19. package/task-executor.mjs +190 -8
  20. package/ui/app.js +418 -38
  21. package/ui/app.monolith.js +4 -4
  22. package/ui/components/agent-selector.js +119 -11
  23. package/ui/components/chat-view.js +142 -14
  24. package/ui/components/diff-viewer.js +13 -12
  25. package/ui/components/forms.js +1 -1
  26. package/ui/components/kanban-board.js +172 -15
  27. package/ui/components/session-list.js +6 -5
  28. package/ui/components/workspace-switcher.js +5 -4
  29. package/ui/demo.html +2167 -328
  30. package/ui/index.html +39 -29
  31. package/ui/modules/api.js +4 -1
  32. package/ui/modules/icon-utils.js +190 -0
  33. package/ui/modules/icons.js +554 -0
  34. package/ui/modules/router.js +1 -0
  35. package/ui/modules/settings-schema.js +13 -13
  36. package/ui/modules/streaming.js +4 -3
  37. package/ui/modules/voice.js +294 -0
  38. package/ui/setup.html +122 -24
  39. package/ui/styles/base.css +4 -1
  40. package/ui/styles/components.css +1256 -149
  41. package/ui/styles/kanban.css +245 -0
  42. package/ui/styles/layout.css +330 -8
  43. package/ui/styles/sessions.css +330 -32
  44. package/ui/styles/variables.css +38 -1
  45. package/ui/styles/workspace-switcher.css +6 -0
  46. package/ui/styles.css +6 -0
  47. package/ui/tabs/agents.js +37 -26
  48. package/ui/tabs/chat.js +36 -13
  49. package/ui/tabs/control.js +35 -9
  50. package/ui/tabs/dashboard.js +19 -12
  51. package/ui/tabs/infra.js +11 -10
  52. package/ui/tabs/logs.js +11 -10
  53. package/ui/tabs/settings.js +17 -16
  54. package/ui/tabs/tasks.js +831 -48
  55. package/ui/tabs/workflows.js +1588 -0
  56. package/ui/vendor/es-module-shims.js +1063 -0
  57. package/ui/vendor/htm.js +1 -0
  58. package/ui/vendor/preact-compat.js +2 -0
  59. package/ui/vendor/preact-hooks.js +2 -0
  60. package/ui/vendor/preact-signals-core.js +1 -0
  61. package/ui/vendor/preact-signals.js +1 -0
  62. package/ui/vendor/preact.js +2 -0
  63. package/ui-server.mjs +635 -17
  64. package/vendor-sync.mjs +241 -0
package/.env.example CHANGED
@@ -332,9 +332,9 @@ TELEGRAM_MINIAPP_ENABLED=false
332
332
  # by the next agent working on that branch. Default: true
333
333
  # TASK_UPSTREAM_SYNC_MAIN=true
334
334
 
335
- # ─── GitHub App (Bosun[botswain] Identity + Auth) ────────────────────────────
336
- # App: https://github.com/apps/bosun-botswain (slug: bosun-botswain)
337
- # Bot identity: bosun-botswain[bot] (appears as contributor on every agent commit)
335
+ # ─── GitHub App (Bosun[VE] Identity + Auth) ────────────────────────────
336
+ # App: https://github.com/apps/bosun-ve (slug: bosun-ve)
337
+ # Bot identity: bosun-ve[bot] (appears as contributor on every agent commit)
338
338
  #
339
339
  # Numeric App ID (shown on the App settings page under "About"):
340
340
  # BOSUN_GITHUB_APP_ID=2911413
@@ -350,9 +350,9 @@ TELEGRAM_MINIAPP_ENABLED=false
350
350
  # BOSUN_GITHUB_WEBHOOK_SECRET=
351
351
  #
352
352
  # Path to the PEM private key downloaded from App settings → Generate a private key:
353
- # BOSUN_GITHUB_PRIVATE_KEY_PATH=/path/to/bosun-botswain.pem
353
+ # BOSUN_GITHUB_PRIVATE_KEY_PATH=/path/to/bosun-ve.pem
354
354
  #
355
- # ─── GitHub App Settings (enable all three in https://github.com/settings/apps/bosun-botswain) ────
355
+ # ─── GitHub App Settings (enable all three in https://github.com/settings/apps/bosun-ve) ────
356
356
  # ✅ Callback URL → http://127.0.0.1:54317/github/callback (set this FIRST, then Save)
357
357
  # ✅ "Request user authorization (OAuth) during installation" → ON
358
358
  # GitHub does OAuth at install time, redirecting to the Callback URL with
@@ -435,6 +435,12 @@ TELEGRAM_MINIAPP_ENABLED=false
435
435
  # BOSUN_ENFORCE_TASK_LABEL=true
436
436
  # Optional issue fetch cap per sync/poll cycle (default: 1000)
437
437
  # GITHUB_ISSUES_LIST_LIMIT=1000
438
+ # Task context limits (comments + attachments)
439
+ # BOSUN_TASK_CONTEXT_MAX_COMMENTS=8
440
+ # BOSUN_TASK_CONTEXT_MAX_COMMENT_CHARS=1200
441
+ # BOSUN_TASK_CONTEXT_MAX_ATTACHMENTS=20
442
+ # Max upload size for task/chat attachments (MB)
443
+ # BOSUN_ATTACHMENT_MAX_MB=25
438
444
 
439
445
  # Jira backend (KANBAN_BACKEND=jira)
440
446
  # Jira Cloud site URL (no trailing slash)
package/README.md CHANGED
@@ -36,6 +36,7 @@ bosun --setup
36
36
  ```
37
37
 
38
38
  Requires:
39
+
39
40
  - Node.js 18+
40
41
  - Git
41
42
  - Bash (for `.sh` wrappers) or PowerShell 7+ (for `.ps1` wrappers)
@@ -60,6 +61,7 @@ Requires:
60
61
  **Source docs (markdown):** `_docs/` is the source of truth for long-form documentation. The website should be generated from the same markdown content so docs stay in sync.
61
62
 
62
63
  Key references:
64
+
63
65
  - [GitHub adapter enhancements](_docs/KANBAN_GITHUB_ENHANCEMENT.md)
64
66
  - [GitHub Projects v2 index](_docs/GITHUB_PROJECTS_V2_INDEX.md)
65
67
  - [GitHub Projects v2 quickstart](_docs/GITHUB_PROJECTS_V2_QUICKSTART.md)
@@ -79,6 +81,7 @@ Bosun enforces a strict quality pipeline in both local hooks and CI:
79
81
 
80
82
  - **Pre-commit hooks** auto-format and lint staged files.
81
83
  - **Pre-push hooks** run targeted checks based on changed files (Go, portal, docs).
84
+ - **Demo load smoke test** runs in `npm test` and blocks push if `site/indexv2.html` or `site/ui/demo.html` fails to load required assets.
82
85
  - **Prepublish checks** validate package contents and release readiness.
83
86
 
84
87
  Local commands you can run any time:
@@ -785,6 +785,7 @@ export class AgentEventBus {
785
785
  branchName:
786
786
  result?.branch || task?.branchName || task?.meta?.branch_name,
787
787
  description: task?.description || "",
788
+ taskContext: task?._taskContextBlock || task?.meta?.taskContextBlock || "",
788
789
  });
789
790
  this.emit(AGENT_EVENT.AUTO_REVIEW, taskId, {
790
791
  title: task?.title || "",
package/agent-prompts.mjs CHANGED
@@ -119,6 +119,12 @@ const PROMPT_DEFS = [
119
119
  description:
120
120
  "Task management agent prompt with full CRUD access via CLI and REST API.",
121
121
  },
122
+ {
123
+ key: "frontendAgent",
124
+ filename: "frontend-agent.md",
125
+ description:
126
+ "Front-end specialist agent with screenshot-based validation and visual verification.",
127
+ },
122
128
  ];
123
129
 
124
130
  export const AGENT_PROMPT_DEFINITIONS = Object.freeze(
@@ -234,6 +240,7 @@ You are the always-on reliability guardian for bosun in devmode.
234
240
 
235
241
  ## Description
236
242
  {{TASK_DESCRIPTION}}
243
+ {{TASK_CONTEXT}}
237
244
 
238
245
  ## Environment
239
246
  - Working Directory: {{WORKTREE_PATH}}
@@ -320,6 +327,7 @@ Please:
320
327
 
321
328
  Original task description:
322
329
  {{TASK_DESCRIPTION}}
330
+ {{TASK_CONTEXT}}
323
331
  `,
324
332
  taskExecutorContinueHasCommits: `# {{TASK_ID}} — CONTINUE (Verify and Push)
325
333
 
@@ -330,6 +338,7 @@ You already made commits.
330
338
  2. If passing, push: git push origin HEAD
331
339
  3. If failing, fix issues, commit, and push.
332
340
  4. Task is not complete until push succeeds.
341
+ {{TASK_CONTEXT}}
333
342
  `,
334
343
  taskExecutorContinueHasEdits: `# {{TASK_ID}} — CONTINUE (Commit and Push)
335
344
 
@@ -340,6 +349,7 @@ You made file edits but no commit yet.
340
349
  2. Run relevant tests.
341
350
  3. Commit with conventional format.
342
351
  4. Push: git push origin HEAD
352
+ {{TASK_CONTEXT}}
343
353
  `,
344
354
  taskExecutorContinueNoProgress: `# CONTINUE - Resume Implementation
345
355
 
@@ -354,6 +364,7 @@ Execute now:
354
364
 
355
365
  Task: {{TASK_TITLE}}
356
366
  Description: {{TASK_DESCRIPTION}}
367
+ {{TASK_CONTEXT}}
357
368
  `,
358
369
  reviewer: `You are a senior code reviewer for a production software project.
359
370
 
@@ -379,6 +390,7 @@ Review the following PR diff for CRITICAL issues ONLY.
379
390
 
380
391
  ## Task Description
381
392
  {{TASK_DESCRIPTION}}
393
+ {{TASK_CONTEXT}}
382
394
 
383
395
  ## Response Format
384
396
  Respond with JSON only:
@@ -604,6 +616,75 @@ Types: feat, fix, docs, refactor, test, chore
604
616
  Statuses: draft → todo → inprogress → inreview → done
605
617
 
606
618
  See .bosun/agents/task-manager.md for full documentation.
619
+ `,
620
+ frontendAgent: `# Frontend Specialist Agent
621
+
622
+ You are a **front-end development specialist** agent managed by Bosun.
623
+
624
+ ## Core Responsibilities
625
+
626
+ 1. Implement HTML, CSS, and JavaScript/TypeScript UI changes
627
+ 2. Build responsive, accessible UI components
628
+ 3. Ensure visual accuracy matching specifications
629
+ 4. Validate changes through automated testing AND visual verification
630
+
631
+ ## Special Skills
632
+
633
+ - CSS Grid/Flexbox layout
634
+ - Component architecture (React, Preact, Vue, Svelte, vanilla)
635
+ - Responsive design (mobile-first)
636
+ - Accessibility (WCAG 2.1 AA)
637
+ - CSS animations and transitions
638
+ - Design system adherence
639
+
640
+ ## CRITICAL: Evidence-Based Validation
641
+
642
+ After completing implementation, you MUST collect visual evidence:
643
+
644
+ ### Screenshot Protocol
645
+ 1. Start the dev server if not already running
646
+ 2. Navigate to every page/component you modified
647
+ 3. Take screenshots at THREE viewport sizes:
648
+ - Desktop (1920×1080)
649
+ - Tablet (768×1024)
650
+ - Mobile (375×812)
651
+ 4. Save ALL screenshots to \`.bosun/evidence/\` directory
652
+ 5. Use descriptive filenames: \`<page>-<viewport>-<timestamp>.png\`
653
+ 6. Also screenshot any interactive states (modals, dropdowns, hover states)
654
+
655
+ ### Evidence Naming Convention
656
+ \`\`\`
657
+ .bosun/evidence/
658
+ homepage-desktop-1234567890.png
659
+ homepage-tablet-1234567890.png
660
+ homepage-mobile-1234567890.png
661
+ modal-open-desktop-1234567890.png
662
+ dark-mode-desktop-1234567890.png
663
+ \`\`\`
664
+
665
+ ## Workflow
666
+ 1. Read task requirements and any linked designs/specs
667
+ 2. Load relevant skills from \`.bosun/skills/\`
668
+ 3. Implement frontend changes
669
+ 4. Run build: \`npm run build\` (zero errors AND zero warnings)
670
+ 5. Run lint: \`npm run lint\`
671
+ 6. Run tests: \`npm test\`
672
+ 7. Start dev server and collect screenshots (see protocol above)
673
+ 8. Commit with conventional format: \`feat(ui): ...\` or \`fix(ui): ...\`
674
+ 9. Push branch
675
+
676
+ ## IMPORTANT: Do NOT mark the task complete
677
+ The Bosun workflow engine handles completion verification.
678
+ An independent model will review your screenshots against the task
679
+ requirements before the task is marked as done.
680
+
681
+ ## Task Context
682
+ - Task: {{TASK_TITLE}}
683
+ - Description: {{TASK_DESCRIPTION}}
684
+ - Branch: {{BRANCH}}
685
+ - Working Directory: {{WORKTREE_PATH}}
686
+
687
+ {{COAUTHOR_INSTRUCTION}}
607
688
  `,
608
689
  };
609
690
 
@@ -670,19 +751,40 @@ export function getDefaultPromptTemplate(key) {
670
751
  return DEFAULT_PROMPTS[key] || "";
671
752
  }
672
753
 
673
- export function renderPromptTemplate(template, values = {}) {
754
+ export function renderPromptTemplate(template, values = {}, rootDir) {
674
755
  if (typeof template !== "string") return "";
675
756
  const normalized = {};
676
757
  for (const [k, v] of Object.entries(values || {})) {
677
758
  normalized[String(k).trim().toUpperCase()] = normalizeTemplateValue(v);
678
759
  }
679
760
 
680
- return template.replace(/\{\{\s*([A-Za-z0-9_]+)\s*\}\}/g, (full, key) => {
761
+ // Resolve namespaced library refs: {{prompt:name}}, {{agent:name}}, {{skill:name}}
762
+ let result = template;
763
+ if (rootDir && _libraryResolver) {
764
+ try {
765
+ result = _libraryResolver(result, rootDir, {});
766
+ } catch {
767
+ // library resolver failed — skip namespaced refs
768
+ }
769
+ }
770
+
771
+ return result.replace(/\{\{\s*([A-Za-z0-9_]+)\s*\}\}/g, (full, key) => {
681
772
  const hit = normalized[String(key).toUpperCase()];
682
773
  return hit == null ? "" : hit;
683
774
  });
684
775
  }
685
776
 
777
+ /**
778
+ * Register the library reference resolver. Called by library-manager or at
779
+ * startup to enable {{prompt:name}}, {{agent:name}}, {{skill:name}} syntax.
780
+ *
781
+ * @param {(template: string, rootDir: string, vars: Object) => string} resolver
782
+ */
783
+ let _libraryResolver = null;
784
+ export function setLibraryResolver(resolver) {
785
+ _libraryResolver = typeof resolver === "function" ? resolver : null;
786
+ }
787
+
686
788
  export function resolvePromptTemplate(template, values, fallback) {
687
789
  const base = typeof fallback === "string" ? fallback : "";
688
790
  if (typeof template !== "string" || !template.trim()) return base;
@@ -1211,3 +1211,57 @@ export function createAnomalyDetector(options = {}) {
1211
1211
  */
1212
1212
 
1213
1213
  export default AnomalyDetector;
1214
+
1215
+ // ── Workflow Engine Integration ─────────────────────────────────────────────
1216
+
1217
+ /**
1218
+ * Registered workflow engine reference for anomaly → workflow triggers.
1219
+ * @type {import("./workflow-engine.mjs").WorkflowEngine | null}
1220
+ */
1221
+ let _workflowEngine = null;
1222
+
1223
+ /**
1224
+ * Register a workflow engine to receive anomaly trigger events.
1225
+ * When set, every anomaly emitted will also call engine.evaluateTriggers("anomaly", anomalyData).
1226
+ *
1227
+ * @param {import("./workflow-engine.mjs").WorkflowEngine} engine
1228
+ */
1229
+ export function setWorkflowEngine(engine) {
1230
+ _workflowEngine = engine;
1231
+ }
1232
+
1233
+ /** Get the currently registered workflow engine (or null) */
1234
+ export function getWorkflowEngine() {
1235
+ return _workflowEngine;
1236
+ }
1237
+
1238
+ /**
1239
+ * Create an onAnomaly callback that also fires workflow triggers.
1240
+ * Wraps the user's original callback and additionally invokes
1241
+ * evaluateTriggers on the registered workflow engine.
1242
+ *
1243
+ * @param {(anomaly: Anomaly) => void} [originalCallback]
1244
+ * @returns {(anomaly: Anomaly) => void}
1245
+ */
1246
+ export function wrapAnomalyCallback(originalCallback) {
1247
+ return (anomaly) => {
1248
+ // Always call the original callback
1249
+ if (originalCallback) originalCallback(anomaly);
1250
+
1251
+ // Fire workflow triggers if engine is registered
1252
+ if (_workflowEngine?.evaluateTriggers) {
1253
+ try {
1254
+ _workflowEngine.evaluateTriggers("anomaly", {
1255
+ anomalyType: anomaly.type,
1256
+ severity: anomaly.severity,
1257
+ agentId: anomaly.processId || anomaly.shortId,
1258
+ message: anomaly.message,
1259
+ action: anomaly.action,
1260
+ taskTitle: anomaly.taskTitle,
1261
+ data: anomaly.data,
1262
+ timestamp: Date.now(),
1263
+ });
1264
+ } catch { /* workflow trigger errors should not break anomaly detection */ }
1265
+ }
1266
+ };
1267
+ }
package/codex-config.mjs CHANGED
@@ -1618,6 +1618,110 @@ export function printConfigSummary(result, log = console.log) {
1618
1618
  log(` Config: ${result.path}`);
1619
1619
  }
1620
1620
 
1621
+ // ── Trusted Projects ─────────────────────────────────────────────────────────
1622
+
1623
+ /**
1624
+ * Escape a string for use inside a double-quoted TOML basic string.
1625
+ * Handles backslashes (Windows paths) and double-quote characters.
1626
+ */
1627
+ function tomlEscapeStr(s) {
1628
+ return String(s).replace(/\\/g, "\\\\").replace(/"/g, '\\"');
1629
+ }
1630
+
1631
+ /**
1632
+ * Format an array of strings as a TOML array literal, correctly escaping
1633
+ * backslashes so Windows paths are stored faithfully.
1634
+ *
1635
+ * Example output: ["C:\\Users\\jon\\bosun", "/home/jon/bosun"]
1636
+ */
1637
+ function formatTomlArrayEscaped(values) {
1638
+ return `[${values.map((v) => `"${tomlEscapeStr(v)}"`).join(", ")}]`;
1639
+ }
1640
+
1641
+ /**
1642
+ * Parse a TOML basic-string array literal, unescaping backslash sequences.
1643
+ */
1644
+ function parseTomlArrayLiteralEscaped(raw) {
1645
+ if (!raw) return [];
1646
+ const inner = raw.trim().replace(/^\[/, "").replace(/\]$/, "");
1647
+ if (!inner.trim()) return [];
1648
+ // Split on commas that are NOT inside quotes
1649
+ const items = [];
1650
+ let buf = "";
1651
+ let inStr = false;
1652
+ for (let i = 0; i < inner.length; i++) {
1653
+ const ch = inner[i];
1654
+ if (ch === "\\" && inStr) { buf += ch + (inner[++i] || ""); continue; }
1655
+ if (ch === '"') { inStr = !inStr; buf += ch; continue; }
1656
+ if (ch === "," && !inStr) { items.push(buf.trim()); buf = ""; continue; }
1657
+ buf += ch;
1658
+ }
1659
+ if (buf.trim()) items.push(buf.trim());
1660
+ return items
1661
+ .map((item) => item.replace(/^"(.*)"$/s, "$1")) // strip outer quotes
1662
+ .map((item) => item.replace(/\\(["\\])/g, "$1")) // unescape \" and \\
1663
+ .filter(Boolean);
1664
+ }
1665
+
1666
+ /**
1667
+ * Ensure the given directory paths are listed in the `trusted_projects`
1668
+ * top-level key in ~/.codex/config.toml.
1669
+ *
1670
+ * Codex refuses to load a per-project .codex/config.toml unless the project
1671
+ * directory appears in this list — producing warnings like:
1672
+ * "⚠ Project config.toml files are disabled … add <dir> as a trusted project"
1673
+ *
1674
+ * Paths are stored as-is (forward or back slashes preserved) with proper TOML
1675
+ * escaping so Windows paths survive round-trips through the file.
1676
+ *
1677
+ * @param {string[]} paths Absolute directories to trust (e.g. [bosunHome])
1678
+ * @param {{ dryRun?: boolean }} [opts]
1679
+ * @returns {{ added: string[], already: string[], path: string }}
1680
+ */
1681
+ export function ensureTrustedProjects(paths, { dryRun = false } = {}) {
1682
+ const result = { added: [], already: [], path: CONFIG_PATH };
1683
+ const desired = (paths || []).map((p) => resolve(p)).filter(Boolean);
1684
+ if (desired.length === 0) return result;
1685
+
1686
+ let toml = readCodexConfig() || "";
1687
+
1688
+ // Parse existing trusted_projects (multi-line arrays may span lines)
1689
+ const existingMatch = toml.match(/^trusted_projects\s*=\s*(\[[^\]]*\])/m);
1690
+ const existing = existingMatch ? parseTomlArrayLiteralEscaped(existingMatch[1]) : [];
1691
+
1692
+ let changed = false;
1693
+ for (const p of desired) {
1694
+ if (existing.includes(p)) {
1695
+ result.already.push(p);
1696
+ } else {
1697
+ existing.push(p);
1698
+ result.added.push(p);
1699
+ changed = true;
1700
+ }
1701
+ }
1702
+
1703
+ if (!changed) return result;
1704
+ if (dryRun) return result;
1705
+
1706
+ const newLine = `trusted_projects = ${formatTomlArrayEscaped(existing)}`;
1707
+
1708
+ if (existingMatch) {
1709
+ toml = toml.replace(/^trusted_projects\s*=\s*\[[^\]]*\]/m, newLine);
1710
+ } else {
1711
+ // Insert before the first section header (or at top if no sections)
1712
+ const firstSection = toml.search(/^\[/m);
1713
+ if (firstSection === -1) {
1714
+ toml = `${newLine}\n${toml}`;
1715
+ } else {
1716
+ toml = `${toml.slice(0, firstSection)}${newLine}\n\n${toml.slice(firstSection)}`;
1717
+ }
1718
+ }
1719
+
1720
+ mkdirSync(CODEX_DIR, { recursive: true });
1721
+ writeFileSync(CONFIG_PATH, toml, "utf8");
1722
+ return result;
1723
+ }
1724
+
1621
1725
  // ── Internal Helpers ─────────────────────────────────────────────────────────
1622
1726
 
1623
1727
  function escapeRegex(str) {
@@ -2,7 +2,7 @@
2
2
  /**
3
3
  * github-app-auth.mjs — GitHub App JWT + Installation Token helpers
4
4
  *
5
- * Provides credential helpers for the Bosun[botswain] GitHub App:
5
+ * Provides credential helpers for the Bosun[VE] GitHub App:
6
6
  * - signAppJWT() — RS256 JWT proving Bosun IS the app
7
7
  * - getInstallationToken(installationId) — short-lived install access token
8
8
  * - getInstallationTokenForRepo(owner,repo) — auto-resolves install from repo
@@ -90,7 +90,7 @@ export async function getInstallationToken(installationId) {
90
90
  Authorization: `Bearer ${jwt}`,
91
91
  Accept: "application/vnd.github+json",
92
92
  "X-GitHub-Api-Version": "2022-11-28",
93
- "User-Agent": "bosun-botswain",
93
+ "User-Agent": "bosun-ve",
94
94
  },
95
95
  },
96
96
  );
@@ -123,7 +123,7 @@ export async function getInstallationTokenForRepo(owner, repo) {
123
123
  Authorization: `Bearer ${jwt}`,
124
124
  Accept: "application/vnd.github+json",
125
125
  "X-GitHub-Api-Version": "2022-11-28",
126
- "User-Agent": "bosun-botswain",
126
+ "User-Agent": "bosun-ve",
127
127
  },
128
128
  },
129
129
  );
@@ -163,7 +163,7 @@ export async function startDeviceFlow(scope = "repo") {
163
163
  headers: {
164
164
  Accept: "application/json",
165
165
  "Content-Type": "application/x-www-form-urlencoded",
166
- "User-Agent": "bosun-botswain",
166
+ "User-Agent": "bosun-ve",
167
167
  },
168
168
  body: body.toString(),
169
169
  });
@@ -217,7 +217,7 @@ export async function pollDeviceToken(deviceCode) {
217
217
  headers: {
218
218
  Accept: "application/json",
219
219
  "Content-Type": "application/x-www-form-urlencoded",
220
- "User-Agent": "bosun-botswain",
220
+ "User-Agent": "bosun-ve",
221
221
  },
222
222
  body: body.toString(),
223
223
  });
@@ -281,7 +281,7 @@ export async function exchangeOAuthCode(code) {
281
281
  headers: {
282
282
  Accept: "application/json",
283
283
  "Content-Type": "application/x-www-form-urlencoded",
284
- "User-Agent": "bosun-botswain",
284
+ "User-Agent": "bosun-ve",
285
285
  },
286
286
  body: body.toString(),
287
287
  });
@@ -318,7 +318,7 @@ export async function getOAuthUser(accessToken) {
318
318
  Authorization: `Bearer ${accessToken}`,
319
319
  Accept: "application/vnd.github+json",
320
320
  "X-GitHub-Api-Version": "2022-11-28",
321
- "User-Agent": "bosun-botswain",
321
+ "User-Agent": "bosun-ve",
322
322
  },
323
323
  });
324
324
  if (!res.ok) {
@@ -82,7 +82,7 @@ async function verifyToken(token) {
82
82
  Authorization: `Bearer ${token}`,
83
83
  Accept: "application/vnd.github+json",
84
84
  "X-GitHub-Api-Version": "2022-11-28",
85
- "User-Agent": "bosun-botswain",
85
+ "User-Agent": "bosun-ve",
86
86
  },
87
87
  });
88
88
  if (!res.ok) return null;
@@ -162,7 +162,7 @@ export async function getAuthHeaders(options = {}) {
162
162
  Authorization: `Bearer ${token}`,
163
163
  Accept: "application/vnd.github+json",
164
164
  "X-GitHub-Api-Version": "2022-11-28",
165
- "User-Agent": "bosun-botswain",
165
+ "User-Agent": "bosun-ve",
166
166
  };
167
167
  }
168
168
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * github-oauth-portal.mjs — Self-contained OAuth setup portal for Bosun[botswain]
2
+ * github-oauth-portal.mjs — Self-contained OAuth setup portal for Bosun[VE]
3
3
  *
4
4
  * Serves a local HTTP portal on port 54317 (bound to 127.0.0.1) that guides
5
5
  * users through GitHub App installation and OAuth authorisation.
@@ -13,7 +13,7 @@
13
13
  * GET /api/status — JSON status endpoint
14
14
  * GET /api/installations — list App installations (requires App JWT)
15
15
  *
16
- * GitHub App settings (https://github.com/settings/apps/bosun-botswain):
16
+ * GitHub App settings (https://github.com/settings/apps/bosun-ve):
17
17
  * Callback URL: http://127.0.0.1:54317/github/callback ← ONLY URL needed
18
18
  *
19
19
  * Notes:
@@ -54,7 +54,7 @@ import {
54
54
 
55
55
  const DEFAULT_PORT = 54317;
56
56
  const DEFAULT_HOST = "127.0.0.1";
57
- const GITHUB_APP_NAME = "bosun-botswain";
57
+ const GITHUB_APP_NAME = "bosun-ve";
58
58
  const GITHUB_APP_URL = `https://github.com/apps/${GITHUB_APP_NAME}`;
59
59
  const GITHUB_APP_INSTALL_URL = `${GITHUB_APP_URL}/installations/new`;
60
60
  const STATE_FILE = join(homedir(), ".bosun", "github-auth-state.json");
@@ -271,7 +271,7 @@ function htmlPage(title, bodyHtml) {
271
271
  <body>
272
272
  <div class="logo">⚓</div>
273
273
  <h1>Bosun</h1>
274
- <div class="subtitle">GitHub App OAuth Setup Portal · <code>bosun-botswain</code></div>
274
+ <div class="subtitle">GitHub App OAuth Setup Portal · <code>bosun-ve</code></div>
275
275
  ${bodyHtml}
276
276
  <script>
277
277
  function copyToClipboard(text, btn) {
@@ -569,7 +569,7 @@ async function handleSetup(req, res) {
569
569
  Authorization: `Bearer ${jwt}`,
570
570
  Accept: "application/vnd.github+json",
571
571
  "X-GitHub-Api-Version": "2022-11-28",
572
- "User-Agent": "bosun-botswain",
572
+ "User-Agent": "bosun-ve",
573
573
  },
574
574
  },
575
575
  );
@@ -703,7 +703,7 @@ function handleWebhookEvent(eventType, payload) {
703
703
  }
704
704
 
705
705
  const CMD_RE = /\/bosun[ \t]+(\w+)(?:[ \t]+(\S+))?/g;
706
- const MENTION_RE = /@bosun-botswain/i;
706
+ const MENTION_RE = /@bosun-ve/i;
707
707
 
708
708
  function processBosunCommand(body, payload) {
709
709
  if (MENTION_RE.test(body)) {
@@ -766,7 +766,7 @@ async function handleApiInstallations(req, res) {
766
766
  Authorization: `Bearer ${jwt}`,
767
767
  Accept: "application/vnd.github+json",
768
768
  "X-GitHub-Api-Version": "2022-11-28",
769
- "User-Agent": "bosun-botswain",
769
+ "User-Agent": "bosun-ve",
770
770
  },
771
771
  });
772
772