@bridge_gpt/mcp-server 0.2.23 → 0.2.25

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.
@@ -19,7 +19,8 @@ import { getAmToken } from "./client.js";
19
19
  * 2. Repo name set (REPO_NAME)
20
20
  * 3. version config field is an SFCC version
21
21
  * 4. dw.json found / instance unambiguous
22
- * 5. AM token acquisition
22
+ * 5. AM token acquisition (OCAPI Account Manager — NOT log/WebDAV access)
23
+ * 6. SFCC Log Query capability (WebDAV Basic auth — independent of steps 3–5)
23
24
  *
24
25
  * Never throws; each check is caught independently so partial states are
25
26
  * always reported. Output is completely secret-free.
@@ -97,8 +98,37 @@ export async function sfccSetupStatusTool(deps) {
97
98
  tokenStatus = `✗ ${msg}`;
98
99
  }
99
100
  }
100
- lines.push(`5. AM Token: ${tokenStatus}`);
101
- lines.push("\nRun `check_permissions` to probe OCAPI access once steps 1–5 are all green.");
101
+ lines.push(`5. AM Token (OCAPI): ${tokenStatus}`);
102
+ // 6. SFCC Log Query capability WebDAV Basic auth, independent of OCAPI/AM.
103
+ // A single Bridge backend probe (secret-free) reports readiness. This is a
104
+ // separate credential surface: an SFCC repo can have OCAPI working (steps 3–5)
105
+ // while log/WebDAV access is not configured, and vice versa.
106
+ let logQueryStatus = "— Skipped (Bridge API not configured)";
107
+ if (apiKeyOk && repoOk) {
108
+ try {
109
+ const url = deps.buildGetUrl("/sfcc/logs/capability", { repo_name: deps.repoName });
110
+ const resp = await fetch(url, { headers: await deps.getGetHeaders() });
111
+ if (!resp.ok) {
112
+ logQueryStatus = `✗ Could not read (Bridge API ${resp.status})`;
113
+ }
114
+ else {
115
+ const body = (await resp.json());
116
+ if (body?.configured === true) {
117
+ logQueryStatus = "✓ Configured (WebDAV log access ready)";
118
+ }
119
+ else {
120
+ const msg = typeof body?.message === "string" ? body.message : "Not configured";
121
+ logQueryStatus = `✗ ${msg}`;
122
+ }
123
+ }
124
+ }
125
+ catch (err) {
126
+ logQueryStatus = `✗ Resolution error: ${err instanceof Error ? err.message : String(err)}`;
127
+ }
128
+ }
129
+ lines.push(`6. SFCC Log Query (WebDAV): ${logQueryStatus}`);
130
+ lines.push("\nRun `check_permissions` to probe OCAPI access once steps 1–5 are all green. " +
131
+ "Step 6 (log/WebDAV access) is independent and gates `sfcc_log_query`.");
102
132
  return {
103
133
  content: [{ type: "text", text: lines.join("\n") }],
104
134
  };
@@ -1469,9 +1469,10 @@ const defaultPruneStaleLaunchScriptsDeps = {
1469
1469
  * recursively. Newer entries and unrelated files/dirs are left untouched.
1470
1470
  *
1471
1471
  * Fully fail-open: any error (missing parent dir, unreadable entry, stat/unlink
1472
- * failure) is swallowed and never blocks or aborts a spawn the same fail-open
1473
- * discipline as the launch-script writer fallback in
1474
- * {@link materializeWorkerLaunchCommand}.
1472
+ * failure) is swallowed and never blocks or aborts a spawn. (Unconditionally so,
1473
+ * unlike {@link materializeWorkerLaunchCommand}, whose inline fallback is gated on
1474
+ * {@link MAX_TERMINAL_COMMAND_BYTES} — pruning a stale temp dir has no launchable/
1475
+ * unlaunchable axis to gate on.)
1475
1476
  */
1476
1477
  export async function pruneStaleLaunchScripts(deps = defaultPruneStaleLaunchScriptsDeps) {
1477
1478
  try {
@@ -1523,27 +1524,54 @@ export const defaultWriteWorkerLaunchScript = async ({ platform, key, content, }
1523
1524
  await writeFile(file, content, { mode: 0o600 });
1524
1525
  return file;
1525
1526
  };
1527
+ /**
1528
+ * Canonical maximum UTF-8 byte length of a single command line handed to a
1529
+ * terminal spawner. macOS `osascript`-driven Terminal/iTerm keystroke delivery
1530
+ * silently truncates (or mangles) a longer line, so a command at or below this
1531
+ * bound is "known-launchable" and anything above it is "known-unlaunchable".
1532
+ *
1533
+ * Exported so the launcher and every install-flow guard read the SAME number:
1534
+ * two independently-written literals would silently drift apart, and the whole
1535
+ * point of the bound is that the launcher's fallback decision and the caller's
1536
+ * final pre-spawn check agree.
1537
+ */
1538
+ export const MAX_TERMINAL_COMMAND_BYTES = 1024;
1526
1539
  /**
1527
1540
  * Resolve the command actually handed to the terminal spawner for one worker.
1528
1541
  * When `deps.writeWorkerLaunchScript` is provided, the full command is persisted
1529
- * to a script and a short `source <path>` runner is returned; if writing fails
1530
- * for any reason the original inline command is returned (fail-open — a launch
1531
- * never aborts because a temp file could not be written). When the seam is
1542
+ * to a script and a short `source <path>` runner is returned. When the seam is
1532
1543
  * absent, the inline command is returned unchanged (legacy behaviour).
1544
+ *
1545
+ * The fail-open on a failed write is CONDITIONAL, and the condition is the point.
1546
+ * Falling back to the inline command is only a fallback if the terminal can
1547
+ * actually run it: above {@link MAX_TERMINAL_COMMAND_BYTES} the spawner truncates
1548
+ * the line, so "fail-open" would deliver a corrupted command — a silent, confusing
1549
+ * failure strictly worse than a loud one. So a failed write falls back inline only
1550
+ * at or below the bound, and returns a structured failure above it.
1533
1551
  */
1534
1552
  export async function materializeWorkerLaunchCommand(deps, key, fullCommand) {
1535
1553
  if (!deps.writeWorkerLaunchScript)
1536
- return fullCommand;
1554
+ return { ok: true, command: fullCommand };
1537
1555
  try {
1538
1556
  const scriptPath = await deps.writeWorkerLaunchScript({
1539
1557
  platform: deps.platform,
1540
1558
  key,
1541
1559
  content: buildLaunchScriptContent(deps.platform, fullCommand),
1542
1560
  });
1543
- return buildLaunchScriptRunnerCommand(deps.platform, scriptPath);
1561
+ return { ok: true, command: buildLaunchScriptRunnerCommand(deps.platform, scriptPath) };
1544
1562
  }
1545
1563
  catch {
1546
- return fullCommand;
1564
+ // Bytes, not `.length`: the bound is a byte bound, and a multibyte prompt can
1565
+ // sit under 1024 JS characters while being well over 1024 UTF-8 bytes.
1566
+ if (Buffer.byteLength(fullCommand, "utf8") <= MAX_TERMINAL_COMMAND_BYTES) {
1567
+ return { ok: true, command: fullCommand };
1568
+ }
1569
+ return {
1570
+ ok: false,
1571
+ reason: "launch-script-write-failed-oversized-command",
1572
+ error: "Could not write the temporary launch script, and the full command is too long to send " +
1573
+ "to the terminal directly. Check that the system temporary directory is writable.",
1574
+ };
1547
1575
  }
1548
1576
  }
1549
1577
  // ---------------------------------------------------------------------------
@@ -1569,10 +1597,17 @@ export async function spawnTabsForCreatedWorktrees(deps, rows, terminal, buildSh
1569
1597
  // agents, or conductor disabled). Never mutates process/global env.
1570
1598
  const shellCommand = injectConductorEnvIntoShellCommand(deps.platform, baseShellCommand, row.conductorEnv);
1571
1599
  // Deliver the (potentially multi-KB) command via a launch-script file so the
1572
- // terminal spawn payload stays tiny and escaping-immune. Fail-open / no-op
1573
- // when no writer seam is configured (see materializeWorkerLaunchCommand).
1574
- const runnableCommand = await materializeWorkerLaunchCommand(deps, row.key, shellCommand);
1575
- const result = await deps.spawnTerminalTab(deps, terminal, runnableCommand, {
1600
+ // terminal spawn payload stays tiny and escaping-immune. No-op when no writer
1601
+ // seam is configured; a failed write falls back inline only while the command
1602
+ // is still launchable (see materializeWorkerLaunchCommand).
1603
+ const materialized = await materializeWorkerLaunchCommand(deps, row.key, shellCommand);
1604
+ if (!materialized.ok) {
1605
+ // Known-unlaunchable: spawning the oversized inline command would deliver a
1606
+ // truncated line. Fail this row only — siblings still launch.
1607
+ out.push({ ...row, status: "spawn-failed", error: materialized.error });
1608
+ continue;
1609
+ }
1610
+ const result = await deps.spawnTerminalTab(deps, terminal, materialized.command, {
1576
1611
  key: row.key,
1577
1612
  worktreePath: row.path,
1578
1613
  });
@@ -1,2 +1,2 @@
1
1
  // AUTO-GENERATED — do not edit manually. Regenerate with: npm run build
2
- export const VERSION = "0.2.23";
2
+ export const VERSION = "0.2.25";
@@ -8,7 +8,7 @@ involve Conductor — you opt in per run with `--conductor`.
8
8
  This document is the reference for Conductor's architecture, epic setup,
9
9
  observability stream, local git hooks, and the per-repo done-gate / auto-merge
10
10
  config. For the everyday `start-tickets` flags and cross-platform behavior, see
11
- [README → CLI Subcommands](./README.md#cli-subcommands).
11
+ [README → CLI Subcommands](../README.md#cli-subcommands).
12
12
 
13
13
  ## Epic Conductor v2 — how an epic is actually driven
14
14
 
@@ -81,7 +81,7 @@ blocks or aborts a spawn, and `--dry-run` performs no conductor side effects.
81
81
  When `--conductor` is set, the spawn boundary also injects
82
82
  `BRIDGE_MCP_PROFILE=conductor` so each worker registers the 8 conductor/event/
83
83
  supervisor MCP tools (a plain `start-tickets` run stays on the default `core`
84
- profile). See [README → Environment Variables](./README.md#environment-variables).
84
+ profile). See [README → Environment Variables](../README.md#environment-variables).
85
85
 
86
86
  ## `conductor install-git-hooks` (BAPI-395)
87
87
 
@@ -0,0 +1,252 @@
1
+ # Installing the Bridge GitHub App
2
+
3
+ Bridge connects to GitHub through a **GitHub App**, not a personal access token. Once
4
+ the app is installed on your repository and linked to your Bridge project, Bridge can
5
+ open pull requests, run automated code review, read CI check status, and (when enabled)
6
+ auto-merge — all using short-lived, per-call installation tokens. Your code and
7
+ credentials stay on GitHub; Bridge mints a fresh token for each operation and stores no
8
+ long-lived GitHub token.
9
+
10
+ ## The app
11
+
12
+ | | |
13
+ |---|---|
14
+ | **Name** | Bridge GPT - AI Tools for SFCC |
15
+ | **Owner** | [@Bridge-GPT](https://github.com/Bridge-GPT) |
16
+ | **Public install page** | <https://github.com/apps/bridge-gpt-ai-tools-for-sfcc> |
17
+ | **App ID** | `954077` |
18
+ | **Client ID** | `Iv23liBEyDUeD25W06ix` |
19
+
20
+ > The App ID and Client ID are **public** identifiers (GitHub shows them on the app's
21
+ > settings page). They are not secrets. Only the app's **private key** and webhook
22
+ > secret are sensitive, and those are held by whoever operates the Bridge deployment —
23
+ > see [Self-hosting / operator setup](#self-hosting--operator-setup) at the end. As an
24
+ > end user you never handle them.
25
+
26
+ ## Prerequisites
27
+
28
+ - A GitHub repository you want Bridge to work on.
29
+ - Permission to install a GitHub App on the account that owns it:
30
+ - **Personal repo:** you can install it yourself.
31
+ - **Organization repo:** you must be an **organization owner**, or a member who can
32
+ *request* the install for an owner to approve. (GitHub decides which button you see —
33
+ **Install**, **Install & request**, or **Request** — based on your role.)
34
+ - Your Bridge project already registered (you have a Bridge API key and repo name).
35
+
36
+ ---
37
+
38
+ ## Option A — `connect-github` from your terminal (recommended)
39
+
40
+ If you already have a Bridge API key configured (you ran `install-bridge`), you can
41
+ connect GitHub without opening the Bridge web UI at all:
42
+
43
+ ```bash
44
+ npx -y @bridge_gpt/mcp-server@latest connect-github --repo <repo_name>
45
+ ```
46
+
47
+ Omit `--repo` and the command infers the project from the current directory, asking you
48
+ to confirm.
49
+
50
+ What happens:
51
+
52
+ 1. The command opens the GitHub App install screen in your browser.
53
+ 2. On GitHub, choose the **account or organization** that owns the repository, pick the
54
+ repositories under **Repository access** (see [Choosing
55
+ repositories](#choosing-repositories)), and click **Install**.
56
+ 3. GitHub returns to a page that just says *"GitHub connection received — return to your
57
+ terminal."* That page is intentionally blank of detail; your terminal is where the
58
+ flow continues.
59
+ 4. Back in the terminal, the command shows the repositories your installation covers and
60
+ asks which one to connect. **Every connection is confirmed by hand** — even when the
61
+ installation contains exactly one repository. There is no `--yes` flag.
62
+ 5. Choose one, and it prints the connected `owner/repo`.
63
+
64
+ **You are never asked for a GitHub token, password, or installation ID.** You
65
+ authenticate to GitHub in your own browser; the terminal only ever carries a short-lived
66
+ Bridge-issued handle. Bridge never sees a GitHub credential.
67
+
68
+ **If you decline at the confirmation prompt**, nothing is connected and nothing is
69
+ changed — re-run the command whenever you like.
70
+
71
+ **If it times out or fails**, no connection is made. Re-run the command; the request
72
+ expires after about 15 minutes, so a stale attempt is never left half-applied.
73
+
74
+ ### Organization approval
75
+
76
+ If an organization owns the repository and you are not an owner, GitHub sends an
77
+ **approval request** to an owner instead of installing the app. That approval happens
78
+ entirely on GitHub's side and **does not return to your terminal**, so `connect-github`
79
+ cannot wait for it — the original request expires. This is a real limitation, not a bug.
80
+
81
+ Once an owner has approved the install, finish the connection with
82
+ [Option C](#option-c--manual-install--installation-id-fallback) below.
83
+
84
+ ---
85
+
86
+ ## Option B — Connect GitHub button (web UI)
87
+
88
+ Bridge captures the installation automatically — you never copy an ID by hand.
89
+
90
+ 1. Open your project's **Get Started** page in the Bridge web UI. The page opens directly
91
+ on the GitHub connection task — connecting your repository is what the page is for.
92
+ 2. In the **1. Connect GitHub** panel, click **Connect GitHub**. Bridge mints a
93
+ short-lived, single-use link scoped to your project and sends you to GitHub's
94
+ app-install screen.
95
+ 3. On GitHub, choose the **account or organization** that owns the repository.
96
+ 4. Under **Repository access**, choose **Only select repositories** and pick the repo(s)
97
+ you want Bridge to cover (or **All repositories**). See
98
+ [Choosing repositories](#choosing-repositories) below.
99
+ 5. Review the requested permissions and click **Install** (or **Install & request** /
100
+ **Request** if an org owner must approve).
101
+ 6. GitHub redirects you back to Bridge. Bridge verifies the installation directly with
102
+ GitHub, links it to your project, and stores the installation automatically. If the
103
+ installation covers exactly one repo — or one repo clearly matches your project —
104
+ Bridge binds it for you; otherwise it shows a short **repository picker** so you can
105
+ confirm which repo maps to this project.
106
+ 7. The outcome appears in the **1. Connect GitHub** panel itself — you should see
107
+ **GitHub connected** naming the repository Bridge linked.
108
+
109
+ That's it — no manual ID entry. If the automatic link fails for any reason, Bridge tells
110
+ you in that same panel and points you to Option C.
111
+
112
+ Connecting an editor over MCP is **optional** and is not a prerequisite for any of the
113
+ above. If you need it, the Get Started page keeps that setup in a collapsed
114
+ **"Need to connect an editor to the MCP?"** drawer below the GitHub panel — open it only
115
+ if you want it.
116
+
117
+ ---
118
+
119
+ ## Option C — Manual install + Installation ID (fallback)
120
+
121
+ This is the **advanced fallback**, not the normal path — use it only if neither the
122
+ `connect-github` command (Option A) nor the one-click **Connect GitHub** button (Option B)
123
+ is available to you (for example an org admin-approval or GitHub Marketplace install), or
124
+ if automatic linking failed. Get Started links to it from the manual-fallback note under
125
+ the GitHub panel.
126
+
127
+ ### 1. Install the app
128
+
129
+ Go to the public install page and install it on the owning account/organization,
130
+ selecting the repository/repositories you want Bridge to access:
131
+
132
+ <https://github.com/apps/bridge-gpt-ai-tools-for-sfcc/installations/new>
133
+
134
+ (Same repository-selection and permissions-review screen as Option B, steps 3–5.)
135
+
136
+ ### 2. Find the Installation ID
137
+
138
+ The Installation ID is the trailing number in the app's **Configure** URL:
139
+
140
+ - **Personal account:** open **Settings → Applications → Installed GitHub Apps**, click
141
+ **Configure** next to *Bridge GPT - AI Tools for SFCC*. The URL is
142
+ `https://github.com/settings/installations/<INSTALLATION_ID>`.
143
+ - **Organization:** open **Organization Settings → Third-party Access → GitHub Apps**,
144
+ click **Configure** next to the app. The URL is
145
+ `https://github.com/organizations/<ORG>/settings/installations/<INSTALLATION_ID>`.
146
+
147
+ For example, `https://github.com/organizations/Bridge-GPT/settings/installations/61661616`
148
+ has Installation ID **`61661616`**.
149
+
150
+ ### 3. Enter it in Bridge
151
+
152
+ On your project's **Setup** page, in the GitHub section, paste the **Installation ID**
153
+ into the field provided and save. Make sure the project's **version control system** is
154
+ set to `github`. Bridge fills in the account owner and repository from your project
155
+ settings.
156
+
157
+ ---
158
+
159
+ ## Choosing repositories
160
+
161
+ When installing (any option), GitHub asks which repositories the app may access:
162
+
163
+ - **All repositories** — the app can access every current and future repo on the account.
164
+ - **Only select repositories** — pick specific repos from the **Select repositories**
165
+ dropdown. Recommended: grant only the repo(s) you actually want Bridge to work on.
166
+
167
+ You can change this later at any time: **Configure** the installation (paths above),
168
+ adjust **Repository access**, and click **Save**. If the app creates a repository, it is
169
+ automatically granted access to that repo.
170
+
171
+ ## What the app can access
172
+
173
+ At install time GitHub shows the **authoritative** list of permissions the app requests —
174
+ review it there. Functionally, the Bridge integration exercises these GitHub permissions:
175
+
176
+ | Permission | Why |
177
+ |---|---|
178
+ | **Contents** (read & write) | Read repo files for parsing/review; manage branch refs when opening/cleaning up PRs |
179
+ | **Pull requests** (read & write) | List/read PRs and diffs; post review comments and reviews; merge when auto-merge is enabled |
180
+ | **Checks / Commit statuses** (read) | Poll CI check-run and status results for a commit |
181
+ | **Administration** (read) | Read branch-protection required-status-checks to resolve which checks must pass |
182
+ | **Metadata** (read) | Baseline repo metadata; verify the installation's repository list |
183
+ | **Webhook events** | Receive `pull_request`, `installation`, and review/merge events that drive automated review and merge |
184
+
185
+ > These are inferred from the GitHub REST endpoints the integration calls. The exact set
186
+ > the app is *registered* with is shown by GitHub on the install screen; treat that
187
+ > screen as the source of truth.
188
+
189
+ ## Verifying the connection
190
+
191
+ After linking, confirm Bridge can act on the repo:
192
+
193
+ - The Bridge **Setup / integration status** should show version control as connected.
194
+ - A Bridge operation that needs GitHub — e.g. `create_pull_request`, `resolve_ci_checks`,
195
+ or `poll_ci_checks` from the MCP — should succeed rather than return a
196
+ "no VCS connection" refusal. (See
197
+ [MCP Tool Integration Dependencies](./mcp-tool-integrations.md) for which tools require
198
+ a VCS connection.)
199
+
200
+ If a GitHub-dependent tool refuses, the installation isn't linked to that project yet —
201
+ re-run Option A, or set the Installation ID via Option C.
202
+
203
+ ## Managing or removing the app
204
+
205
+ - **Change repo access / review permissions:** **Configure** the installation (URLs
206
+ above) → adjust **Repository access** → **Save**.
207
+ - **Uninstall:** on the same Configure page, scroll to **Danger zone → Uninstall**.
208
+ Uninstalling revokes Bridge's access immediately; existing stored installation IDs stop
209
+ working.
210
+
211
+ ---
212
+
213
+ ## Self-hosting / operator setup
214
+
215
+ *Skip this section if you are an end user connecting to a hosted Bridge deployment — it is
216
+ for whoever runs the Bridge API server.*
217
+
218
+ The GitHub App identity is configured **once per deployment** via environment variables.
219
+ Bridge uses the App private key to mint short-lived installation tokens on demand; it
220
+ persists no long-lived GitHub token.
221
+
222
+ | Env var | Value / purpose |
223
+ |---|---|
224
+ | `GIT_APP_ID` | The app's numeric App ID — `954077` for *Bridge GPT - AI Tools for SFCC*. |
225
+ | `GIT_PRIVATE_KEY` | **base64-encoded PEM** private key generated for the app (GitHub → app settings → *Generate a private key*). This is the one true secret. |
226
+ | `GITHUB_APP_INSTALL_URL` | The public install URL the **Connect GitHub** button sends users to: `https://github.com/apps/bridge-gpt-ai-tools-for-sfcc/installations/new`. |
227
+ | `GITHUB_WEBHOOK_SECRET` | App-level shared secret validating signed `installation` / merge webhooks. If unset, those webhooks are disabled (the redirect-callback path still works). |
228
+ | `BGPT_ENCRYPTION_KEY` | Fernet key used to encrypt per-repo credentials at rest. |
229
+
230
+ The app's **Setup URL** (in GitHub app settings) must point at the deployment's
231
+ `GET /setup/github/callback` endpoint so the post-install redirect can auto-link the
232
+ installation. Webhook endpoints used by the integration include the code-review hook
233
+ (e.g. `https://<deployment-host>/github/code-review`); configure these on the app to match
234
+ your deployment host.
235
+
236
+ > **Note:** the install URL is published from two independent places, and only one reads
237
+ > the environment. `GITHUB_APP_INSTALL_URL` drives the get-started **Connect GitHub**
238
+ > button. The setup *instructions* — the install-instructions API, the capability report's
239
+ > `configure_in`, and the generated README region — come from the `GITHUB_APP_INSTALL_URL`
240
+ > constant in `api/library/config/integration_instructions.py`, which is **not**
241
+ > env-overridable. Setting the env var alone will not correct the instructions. Keep the
242
+ > two in sync; after editing the constant, regenerate the README with
243
+ > `python scripts/sync_integration_readme.py`.
244
+
245
+ ## See also
246
+
247
+ - [MCP Tool Integration Dependencies](./mcp-tool-integrations.md) — which MCP tools need a
248
+ VCS connection (BLOCK) vs merely degrade without one.
249
+ - [Installing the SFCC Integration (OCAPI)](./sfcc-integration.md) — the separate
250
+ Salesforce B2C sandbox integration.
251
+ - GitHub docs: [Installing a GitHub App from a third party](https://docs.github.com/en/apps/using-github-apps/installing-a-github-app-from-a-third-party),
252
+ [Reviewing and modifying installed GitHub Apps](https://docs.github.com/en/apps/using-github-apps/reviewing-and-modifying-installed-github-apps).