@bridge_gpt/mcp-server 0.2.68 → 0.2.69
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +99 -41
- package/build/bridge-permissions.js +627 -94
- package/build/check-update-cli.js +74 -0
- package/build/claude-settings.js +84 -2
- package/build/cli-help.js +7 -1
- package/build/codex-home.js +9 -1
- package/build/codex-launcher-pin.js +598 -0
- package/build/commands.generated.js +5 -5
- package/build/connect-github-api.js +33 -14
- package/build/docs.generated.js +2 -2
- package/build/doctor.js +109 -6
- package/build/epic-implementer-bin.js +80 -85
- package/build/executor/completion-contract.js +2 -0
- package/build/executor/http-client.js +43 -4
- package/build/index.js +464 -246
- package/build/install-bridge.js +260 -297
- package/build/launcher-config-inspection.js +184 -19
- package/build/mcp-host-targets.js +4 -0
- package/build/mcp-identity.js +7 -0
- package/build/pipelines.generated.js +4 -4
- package/build/readme.generated.js +1 -1
- package/build/session-upgrade-notice.js +86 -0
- package/build/start-tickets.js +15 -0
- package/build/tool-call-advisory.js +162 -0
- package/build/tool-surface-gating.js +28 -89
- package/build/update-check.js +97 -16
- package/build/update-status.js +61 -71
- package/build/upgrade-advice.js +15 -11
- package/build/upgrade-cli.js +347 -20
- package/build/version.generated.js +3 -3
- package/package.json +1 -1
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal `check-update` subcommand (BAPI-1446).
|
|
3
|
+
*
|
|
4
|
+
* The Claude Code SessionStart hook runs `check-update --hook claude-session-start`
|
|
5
|
+
* and reads one JSON object from stdout. This command answers from the local
|
|
6
|
+
* update cache only: it never constructs an MCP server and never touches the
|
|
7
|
+
* network, so a cold start stays well inside the hook's timeout.
|
|
8
|
+
*
|
|
9
|
+
* Protocol:
|
|
10
|
+
* - eligible: exactly one newline-terminated JSON object whose
|
|
11
|
+
* `systemMessage` (shown to the person) and
|
|
12
|
+
* `hookSpecificOutput.additionalContext` (seen by the model) carry the same
|
|
13
|
+
* notice text;
|
|
14
|
+
* - silent: no output at all;
|
|
15
|
+
* - always exit 0 in the supported mode, even after an unexpected error, which
|
|
16
|
+
* is contained without logging. A hook must never block a session.
|
|
17
|
+
*
|
|
18
|
+
* Any other argument form is a usage error: a concise stderr message and a
|
|
19
|
+
* nonzero exit.
|
|
20
|
+
*/
|
|
21
|
+
import { getNoticeDecision } from "./session-upgrade-notice.js";
|
|
22
|
+
import { formatSessionUpgradeNotice } from "./upgrade-advice.js";
|
|
23
|
+
import { VERSION } from "./version.generated.js";
|
|
24
|
+
/** The only supported hook mode. */
|
|
25
|
+
export const CLAUDE_SESSION_START_HOOK_MODE = "claude-session-start";
|
|
26
|
+
/** The usage line printed to stderr for an unsupported argument form. */
|
|
27
|
+
export const CHECK_UPDATE_USAGE = `Usage: check-update --hook ${CLAUDE_SESSION_START_HOOK_MODE}`;
|
|
28
|
+
function writeStdout(text) {
|
|
29
|
+
return new Promise((resolve, reject) => {
|
|
30
|
+
process.stdout.write(text, (error) => {
|
|
31
|
+
if (error) {
|
|
32
|
+
reject(error);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
resolve();
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Run the command. Resolves to the process exit code: 0 for the supported hook
|
|
41
|
+
* mode (whether or not it printed anything), 2 for any other argument form.
|
|
42
|
+
*/
|
|
43
|
+
export async function runCheckUpdateCli(argv, options = {}) {
|
|
44
|
+
const stderr = options.stderr ?? ((text) => void process.stderr.write(`${text}\n`));
|
|
45
|
+
const supported = argv.length === 2 && argv[0] === "--hook" && argv[1] === CLAUDE_SESSION_START_HOOK_MODE;
|
|
46
|
+
if (!supported) {
|
|
47
|
+
try {
|
|
48
|
+
stderr(CHECK_UPDATE_USAGE);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// A stderr failure cannot change the usage-error result.
|
|
52
|
+
}
|
|
53
|
+
return 2;
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
const env = options.env ?? process.env;
|
|
57
|
+
const decision = getNoticeDecision(env, options.version ?? VERSION);
|
|
58
|
+
if (decision.show) {
|
|
59
|
+
const notice = formatSessionUpgradeNotice(decision.current, decision.latest);
|
|
60
|
+
const payload = {
|
|
61
|
+
systemMessage: notice,
|
|
62
|
+
hookSpecificOutput: {
|
|
63
|
+
hookEventName: "SessionStart",
|
|
64
|
+
additionalContext: notice,
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
await (options.stdout ?? writeStdout)(`${JSON.stringify(payload)}\n`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
// Contained without logging: a hook must never fail a session start.
|
|
72
|
+
}
|
|
73
|
+
return 0;
|
|
74
|
+
}
|
package/build/claude-settings.js
CHANGED
|
@@ -62,8 +62,16 @@ export function mergeClaudeSettingsWithCommandHook(settings, command, events, op
|
|
|
62
62
|
hooks[event] = entries;
|
|
63
63
|
continue;
|
|
64
64
|
}
|
|
65
|
-
const
|
|
66
|
-
|
|
65
|
+
const leaf = { type: "command", command };
|
|
66
|
+
const timeout = options.eventTimeouts?.[event];
|
|
67
|
+
if (timeout !== undefined)
|
|
68
|
+
leaf.timeout = timeout;
|
|
69
|
+
const newEntry = { hooks: [leaf] };
|
|
70
|
+
const eventMatcher = options.eventMatchers?.[event];
|
|
71
|
+
if (eventMatcher !== undefined) {
|
|
72
|
+
newEntry.matcher = eventMatcher;
|
|
73
|
+
}
|
|
74
|
+
else if (event === "PreToolUse") {
|
|
67
75
|
// Explicit matcher wins; else mirror any existing PreToolUse matcher; else
|
|
68
76
|
// fall back to the broad default.
|
|
69
77
|
newEntry.matcher =
|
|
@@ -75,6 +83,80 @@ export function mergeClaudeSettingsWithCommandHook(settings, command, events, op
|
|
|
75
83
|
}
|
|
76
84
|
return { ...settings, hooks };
|
|
77
85
|
}
|
|
86
|
+
function isPlainObject(value) {
|
|
87
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Shape guard for the hook containers a provisioner is about to write into
|
|
91
|
+
* (BAPI-1446). An ABSENT `hooks` object or event array is fine: it will be
|
|
92
|
+
* created. A PRESENT one of the wrong type (`hooks` that is an array, a string, or
|
|
93
|
+
* null; an event that is an object or a string) is `malformed`, and must be
|
|
94
|
+
* reported and left alone rather than replaced with an empty container.
|
|
95
|
+
*/
|
|
96
|
+
export function inspectClaudeHookContainers(settings, events) {
|
|
97
|
+
const hooks = settings.hooks;
|
|
98
|
+
if (hooks === undefined)
|
|
99
|
+
return "ok";
|
|
100
|
+
if (!isPlainObject(hooks))
|
|
101
|
+
return "malformed";
|
|
102
|
+
for (const event of events) {
|
|
103
|
+
const entries = hooks[event];
|
|
104
|
+
if (entries !== undefined && !Array.isArray(entries))
|
|
105
|
+
return "malformed";
|
|
106
|
+
}
|
|
107
|
+
return "ok";
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* {@link mergeClaudeSettingsWithCommandHook} behind the shape guard. A malformed
|
|
111
|
+
* existing container is refused: nothing is merged and the caller's settings are
|
|
112
|
+
* not touched. Use this for new provisioning; the legacy merge keeps its
|
|
113
|
+
* original lenient behavior for its existing callers.
|
|
114
|
+
*/
|
|
115
|
+
export function mergeClaudeSettingsWithCommandHookChecked(settings, command, events, options = {}) {
|
|
116
|
+
const checked = options.enablePreToolUse && !events.includes("PreToolUse")
|
|
117
|
+
? [...events, "PreToolUse"]
|
|
118
|
+
: events;
|
|
119
|
+
if (inspectClaudeHookContainers(settings, checked) === "malformed") {
|
|
120
|
+
return { ok: false, reason: "malformed-hooks" };
|
|
121
|
+
}
|
|
122
|
+
return { ok: true, settings: mergeClaudeSettingsWithCommandHook(settings, command, events, options) };
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Remove ONE exact command from an event, immutably. Only command leaves whose
|
|
126
|
+
* command string equals `command` are removed; an entry left with no hooks is
|
|
127
|
+
* dropped, and everything else (other leaves, other entries, other events, other
|
|
128
|
+
* settings) is kept as is. Returns the SAME object when nothing matches or the
|
|
129
|
+
* relevant structure is malformed, so a caller can detect "no change" by identity.
|
|
130
|
+
* An emptied event array or `hooks` object is left in place: the user may own it.
|
|
131
|
+
*/
|
|
132
|
+
export function removeClaudeSettingsCommandHook(settings, event, command) {
|
|
133
|
+
const hooks = settings.hooks;
|
|
134
|
+
if (!isPlainObject(hooks))
|
|
135
|
+
return settings;
|
|
136
|
+
const entries = hooks[event];
|
|
137
|
+
if (!Array.isArray(entries))
|
|
138
|
+
return settings;
|
|
139
|
+
let changed = false;
|
|
140
|
+
const nextEntries = [];
|
|
141
|
+
for (const entry of entries) {
|
|
142
|
+
if (!isPlainObject(entry) || !Array.isArray(entry.hooks)) {
|
|
143
|
+
nextEntries.push(entry);
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
const leaves = entry.hooks;
|
|
147
|
+
const kept = leaves.filter((leaf) => !(isPlainObject(leaf) && leaf.type === "command" && leaf.command === command));
|
|
148
|
+
if (kept.length === leaves.length) {
|
|
149
|
+
nextEntries.push(entry);
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
changed = true;
|
|
153
|
+
if (kept.length > 0)
|
|
154
|
+
nextEntries.push({ ...entry, hooks: kept });
|
|
155
|
+
}
|
|
156
|
+
if (!changed)
|
|
157
|
+
return settings;
|
|
158
|
+
return { ...settings, hooks: { ...hooks, [event]: nextEntries } };
|
|
159
|
+
}
|
|
78
160
|
/**
|
|
79
161
|
* Provision (merge into) ONE worktree's `.claude/settings.local.json`. Missing
|
|
80
162
|
* settings are treated as `{}`; malformed JSON is a safety stop (the file is
|
package/build/cli-help.js
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { MCP_PACKAGE_NAME } from "./mcp-identity.js";
|
|
2
2
|
/** The one recovery command shared by terminal guidance surfaces. */
|
|
3
3
|
export const INSTALL_RECOVERY_COMMAND = `npx -y ${MCP_PACKAGE_NAME} install`;
|
|
4
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* Returns static CLI orientation text without resolving configuration or doing I/O.
|
|
6
|
+
*
|
|
7
|
+
* Internal subcommands (`mcp-invoke`, `check-update`) are routed by the
|
|
8
|
+
* dispatcher but are intentionally not listed: they are launched by tools and
|
|
9
|
+
* hooks, not typed by a person.
|
|
10
|
+
*/
|
|
5
11
|
export function getTopLevelCliUsage() {
|
|
6
12
|
return `Usage: bridge-api-mcp-server [command]
|
|
7
13
|
|
package/build/codex-home.js
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import os from "node:os";
|
|
3
|
+
/**
|
|
4
|
+
* True when `CODEX_HOME` is set to an absolute path, which is the only override
|
|
5
|
+
* Codex's user config root accepts. A relative or blank value is ignored.
|
|
6
|
+
*/
|
|
7
|
+
export function isCodexHomeOverrideHonored(env) {
|
|
8
|
+
const override = env.CODEX_HOME?.trim();
|
|
9
|
+
return Boolean(override) && path.isAbsolute(override);
|
|
10
|
+
}
|
|
3
11
|
/** Codex's user config root; only an absolute CODEX_HOME is accepted. */
|
|
4
12
|
export function codexHome(env, homedir = os.homedir()) {
|
|
5
13
|
const override = env.CODEX_HOME?.trim();
|
|
6
|
-
return
|
|
14
|
+
return isCodexHomeOverrideHonored(env) ? override : path.join(homedir, ".codex");
|
|
7
15
|
}
|