@wibeco/bridge 0.1.0 → 0.1.2
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 +3 -0
- package/dist/{chunk-5CBPZCBE.js → chunk-J2HWZMXA.js} +94 -17
- package/dist/{chunk-BL74PDGC.js → chunk-OTDZMJDK.js} +23 -2
- package/dist/cli.js +17 -5
- package/dist/codex-hook.js +2 -2
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/templates/claude-code/README.md +6 -6
- package/templates/codex/README.md +10 -9
- package/templates/cursor/README.md +11 -9
- package/templates/cursor/hooks.json.example +22 -11
package/README.md
CHANGED
|
@@ -30,6 +30,9 @@ npx --yes @wibeco/bridge@latest doctor --repository <owner/repository>
|
|
|
30
30
|
Use `--adapter claude-code` or `--adapter codex` for another supported agent.
|
|
31
31
|
The setup command opens a one-time browser authorization and stores the
|
|
32
32
|
project-scoped device credential in the operating-system keychain.
|
|
33
|
+
Matching reruns reuse that credential and retry verification without creating
|
|
34
|
+
another device. Use `setup --reauthorize` only when doctor reports that the
|
|
35
|
+
saved token was rejected or revoked.
|
|
33
36
|
|
|
34
37
|
`setup` keeps reviewable templates under `.wibe/integrations/<adapter>` and
|
|
35
38
|
installs native project configuration only when the destination file does not
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
normalizeGitHubRepository,
|
|
13
13
|
pollDeviceToken,
|
|
14
14
|
requestDeviceAuthorization
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-OTDZMJDK.js";
|
|
16
16
|
|
|
17
17
|
// src/cli/commands.ts
|
|
18
18
|
import { access, cp, mkdir, readFile, writeFile } from "fs/promises";
|
|
@@ -33,9 +33,7 @@ async function setupCommand(requestedAdapter, options = {}) {
|
|
|
33
33
|
await assertExpectedRepository(cwd, expectedRepository);
|
|
34
34
|
}
|
|
35
35
|
const adapter = await detectAdapter(requestedAdapter, cwd);
|
|
36
|
-
const
|
|
37
|
-
const packagedTemplate = join(packageRoot, "templates", adapter);
|
|
38
|
-
const source = await exists(packagedTemplate) ? packagedTemplate : resolve(packageRoot, "..", "..", "integrations", adapter);
|
|
36
|
+
const source = await resolveTemplateSource(adapter);
|
|
39
37
|
const destination = join(cwd, ".wibe", "integrations", adapter);
|
|
40
38
|
if (!await exists(destination)) {
|
|
41
39
|
await mkdir(join(cwd, ".wibe", "integrations"), { recursive: true });
|
|
@@ -55,6 +53,24 @@ async function setupCommand(requestedAdapter, options = {}) {
|
|
|
55
53
|
`Existing ${projectConfigPath} targets a different project, URL, adapter, or repository. It was not overwritten; remove it intentionally or rerun setup with matching options.`
|
|
56
54
|
);
|
|
57
55
|
}
|
|
56
|
+
const existingCredential = existingProjectConfig ? await loadCredential(cwd) : null;
|
|
57
|
+
if (!options.reauthorize && existingCredential && existingCredential.projectId === options.projectId && existingCredential.appUrl.replace(/\/$/, "") === appUrl) {
|
|
58
|
+
const installedNativeFiles2 = await installNativeConfigs(
|
|
59
|
+
adapter,
|
|
60
|
+
source,
|
|
61
|
+
cwd,
|
|
62
|
+
appUrl
|
|
63
|
+
);
|
|
64
|
+
const heartbeat2 = await sendVerificationHeartbeat(
|
|
65
|
+
existingCredential,
|
|
66
|
+
adapter,
|
|
67
|
+
"setup"
|
|
68
|
+
);
|
|
69
|
+
return {
|
|
70
|
+
exitCode: heartbeat2.error ? 1 : 0,
|
|
71
|
+
message: heartbeat2.error ? `Wibe is already authorized, but verification failed (${heartbeat2.error}). No new device was created. Retry with wibe doctor; if the token is rejected, rerun setup with --reauthorize.` : `Wibe was already authorized and the event connection was verified. Installed ${installedNativeFiles2.length} missing native config file(s); existing configs were left untouched.`
|
|
72
|
+
};
|
|
73
|
+
}
|
|
58
74
|
const authorization = await requestDeviceAuthorization({
|
|
59
75
|
appUrl,
|
|
60
76
|
projectId: options.projectId,
|
|
@@ -213,16 +229,7 @@ async function doctorCommand(cwd = process.cwd(), requestedRepository) {
|
|
|
213
229
|
adapterError = error instanceof Error ? error.message : String(error);
|
|
214
230
|
}
|
|
215
231
|
const heartbeat = credential && adapter && repositoryMatches ? await sendVerificationHeartbeat(credential, adapter, "doctor") : void 0;
|
|
216
|
-
const
|
|
217
|
-
exists(join(cwd, ".cursor", "hooks.json")),
|
|
218
|
-
exists(join(cwd, ".claude", "settings.json")),
|
|
219
|
-
exists(join(cwd, ".codex", "hooks.json"))
|
|
220
|
-
]);
|
|
221
|
-
const nativeMcp = await Promise.all([
|
|
222
|
-
exists(join(cwd, ".cursor", "mcp.json")),
|
|
223
|
-
exists(join(cwd, ".mcp.json")),
|
|
224
|
-
exists(join(cwd, ".codex", "config.toml"))
|
|
225
|
-
]);
|
|
232
|
+
const nativeConfig = adapter ? await validateNativeConfigs(adapter, cwd) : { hooks: false, mcp: false };
|
|
226
233
|
const checks = [
|
|
227
234
|
["node", Number(process.versions.node.split(".")[0]) >= 20],
|
|
228
235
|
["git repository", Boolean(repository)],
|
|
@@ -239,9 +246,12 @@ async function doctorCommand(cwd = process.cwd(), requestedRepository) {
|
|
|
239
246
|
adapterError ? `adapter detection (${adapterError})` : "adapter detection",
|
|
240
247
|
Boolean(adapter)
|
|
241
248
|
],
|
|
242
|
-
["agent hooks",
|
|
243
|
-
["MCP configuration",
|
|
244
|
-
[
|
|
249
|
+
["agent hooks configuration", nativeConfig.hooks],
|
|
250
|
+
["MCP endpoint configuration", nativeConfig.mcp],
|
|
251
|
+
[
|
|
252
|
+
heartbeat?.error ? `verification heartbeat (${heartbeat.error})` : "verification heartbeat",
|
|
253
|
+
Boolean(heartbeat && !heartbeat.error)
|
|
254
|
+
]
|
|
245
255
|
];
|
|
246
256
|
const failures = checks.filter(([, okay]) => !okay).length;
|
|
247
257
|
return {
|
|
@@ -321,6 +331,73 @@ async function exists(path) {
|
|
|
321
331
|
return false;
|
|
322
332
|
}
|
|
323
333
|
}
|
|
334
|
+
async function validateNativeConfigs(adapter, cwd) {
|
|
335
|
+
if (adapter === "codex") {
|
|
336
|
+
const hooks = await validJson(
|
|
337
|
+
join(cwd, ".codex", "hooks.json"),
|
|
338
|
+
(value) => isRecord(value.hooks)
|
|
339
|
+
);
|
|
340
|
+
let mcp = false;
|
|
341
|
+
try {
|
|
342
|
+
const config = await readFile(join(cwd, ".codex", "config.toml"), "utf8");
|
|
343
|
+
mcp = config.includes("[mcp_servers.wibe]") && config.includes("/api/mcp");
|
|
344
|
+
} catch {
|
|
345
|
+
mcp = false;
|
|
346
|
+
}
|
|
347
|
+
return { hooks, mcp };
|
|
348
|
+
}
|
|
349
|
+
const hookPath = adapter === "cursor" ? join(cwd, ".cursor", "hooks.json") : join(cwd, ".claude", "settings.json");
|
|
350
|
+
const mcpPath = adapter === "cursor" ? join(cwd, ".cursor", "mcp.json") : join(cwd, ".mcp.json");
|
|
351
|
+
return {
|
|
352
|
+
hooks: await validJson(hookPath, (value) => isRecord(value.hooks)),
|
|
353
|
+
mcp: await validJson(mcpPath, (value) => {
|
|
354
|
+
if (!isRecord(value.mcpServers)) return false;
|
|
355
|
+
const wibe = value.mcpServers.wibe;
|
|
356
|
+
return isRecord(wibe) && typeof wibe.url === "string" && wibe.url.replace(/\/$/, "").endsWith("/api/mcp");
|
|
357
|
+
})
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
async function validJson(path, predicate) {
|
|
361
|
+
try {
|
|
362
|
+
const value = JSON.parse(await readFile(path, "utf8"));
|
|
363
|
+
return isRecord(value) && predicate(value);
|
|
364
|
+
} catch {
|
|
365
|
+
return false;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
function isRecord(value) {
|
|
369
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
370
|
+
}
|
|
371
|
+
async function resolveTemplateSource(adapter, moduleUrl = import.meta.url) {
|
|
372
|
+
const moduleDir = resolve(fileURLToPath(new URL(".", moduleUrl)));
|
|
373
|
+
const candidates = [
|
|
374
|
+
resolve(moduleDir, ".."),
|
|
375
|
+
resolve(moduleDir, "../..")
|
|
376
|
+
];
|
|
377
|
+
for (const candidate of candidates) {
|
|
378
|
+
try {
|
|
379
|
+
const manifest = JSON.parse(
|
|
380
|
+
await readFile(join(candidate, "package.json"), "utf8")
|
|
381
|
+
);
|
|
382
|
+
if (manifest.name !== "@wibeco/bridge") continue;
|
|
383
|
+
const packagedTemplate = join(candidate, "templates", adapter);
|
|
384
|
+
if (await exists(packagedTemplate)) return packagedTemplate;
|
|
385
|
+
const developmentTemplate = resolve(
|
|
386
|
+
candidate,
|
|
387
|
+
"..",
|
|
388
|
+
"..",
|
|
389
|
+
"integrations",
|
|
390
|
+
adapter
|
|
391
|
+
);
|
|
392
|
+
if (await exists(developmentTemplate)) return developmentTemplate;
|
|
393
|
+
} catch {
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
throw new Error(
|
|
398
|
+
`Could not locate the ${adapter} integration templates relative to the Wibe bridge package.`
|
|
399
|
+
);
|
|
400
|
+
}
|
|
324
401
|
function parseAdapter(value) {
|
|
325
402
|
if (value && ADAPTERS.includes(value)) return value;
|
|
326
403
|
throw new Error(`Adapter must be one of: ${ADAPTERS.join(", ")}`);
|
|
@@ -149,7 +149,13 @@ var events2 = {
|
|
|
149
149
|
"tool-complete": "tool.completed",
|
|
150
150
|
"command-start": "shell.started",
|
|
151
151
|
"command-complete": "shell.completed",
|
|
152
|
-
"file-change": "file.changed"
|
|
152
|
+
"file-change": "file.changed",
|
|
153
|
+
"user-prompt-submit": "lifecycle.before",
|
|
154
|
+
"pre-tool-use": "tool.started",
|
|
155
|
+
"post-tool-use": "tool.completed",
|
|
156
|
+
"subagent-start": "lifecycle.before",
|
|
157
|
+
"subagent-stop": "lifecycle.after",
|
|
158
|
+
stop: "session.ended"
|
|
153
159
|
};
|
|
154
160
|
var safeKeys2 = ["event", "tool_name", "command_type", "model", "reason", "turn_id"];
|
|
155
161
|
function mapCodexHook(eventName, input) {
|
|
@@ -274,7 +280,22 @@ var SignedBatchClient = class {
|
|
|
274
280
|
authorization: `Bearer ${this.options.accessToken}`
|
|
275
281
|
}
|
|
276
282
|
});
|
|
277
|
-
if (!response.ok)
|
|
283
|
+
if (!response.ok) {
|
|
284
|
+
const requestId = response.headers.get("x-request-id");
|
|
285
|
+
let detail = "";
|
|
286
|
+
try {
|
|
287
|
+
const body2 = await response.json();
|
|
288
|
+
const code = typeof body2.code === "string" ? body2.code.slice(0, 80) : void 0;
|
|
289
|
+
const message = typeof body2.error === "string" ? body2.error.slice(0, 160) : void 0;
|
|
290
|
+
detail = [code, message].filter(Boolean).join(": ");
|
|
291
|
+
} catch {
|
|
292
|
+
}
|
|
293
|
+
const suffix = [
|
|
294
|
+
detail ? `: ${detail}` : "",
|
|
295
|
+
requestId ? ` (request ${requestId.slice(0, 80)})` : ""
|
|
296
|
+
].join("");
|
|
297
|
+
throw new Error(`Collector returned HTTP ${response.status}${suffix}`);
|
|
298
|
+
}
|
|
278
299
|
await this.options.queue.remove(events4.map((event) => event.id));
|
|
279
300
|
return { sent: events4.length, remaining: await this.options.queue.size() };
|
|
280
301
|
} catch (error) {
|
package/dist/cli.js
CHANGED
|
@@ -5,16 +5,17 @@ import {
|
|
|
5
5
|
parseAdapter,
|
|
6
6
|
setupCommand,
|
|
7
7
|
statusCommand
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-
|
|
8
|
+
} from "./chunk-J2HWZMXA.js";
|
|
9
|
+
import "./chunk-OTDZMJDK.js";
|
|
10
10
|
|
|
11
11
|
// src/cli.ts
|
|
12
12
|
var HELP = `wibe-bridge <command>
|
|
13
13
|
|
|
14
14
|
Commands:
|
|
15
|
-
setup --project <uuid> [--adapter <cursor|claude-code|codex>] [--url <wibe-url>] [--repository <owner/repo>]
|
|
15
|
+
setup --project <uuid> [--adapter <cursor|claude-code|codex>] [--url <wibe-url>] [--repository <owner/repo>] [--reauthorize]
|
|
16
16
|
Auto-detects one adapter from the environment or repository config.
|
|
17
17
|
Use --adapter when multiple agent configs are present.
|
|
18
|
+
Use --reauthorize only to replace a rejected or revoked device token.
|
|
18
19
|
status
|
|
19
20
|
emit --adapter <name> --event <hook-name> (JSON payload on stdin)
|
|
20
21
|
doctor [--repository <owner/repo>]`;
|
|
@@ -26,7 +27,8 @@ async function main() {
|
|
|
26
27
|
result = await setupCommand(adapterOption ? parseAdapter(adapterOption) : void 0, {
|
|
27
28
|
projectId: option(args, "--project"),
|
|
28
29
|
appUrl: option(args, "--url"),
|
|
29
|
-
expectedRepository: option(args, "--repository")
|
|
30
|
+
expectedRepository: option(args, "--repository"),
|
|
31
|
+
reauthorize: args.includes("--reauthorize")
|
|
30
32
|
});
|
|
31
33
|
} else if (command === "status") {
|
|
32
34
|
result = await statusCommand();
|
|
@@ -35,6 +37,11 @@ async function main() {
|
|
|
35
37
|
const eventName = option(args, "--event");
|
|
36
38
|
if (!eventName) throw new Error("--event is required");
|
|
37
39
|
result = await emitCommand(adapter, eventName, await readJsonStdin());
|
|
40
|
+
process.stderr.write(`${result.message}
|
|
41
|
+
`);
|
|
42
|
+
process.stdout.write("{}\n");
|
|
43
|
+
process.exitCode = 0;
|
|
44
|
+
return;
|
|
38
45
|
} else if (command === "doctor") {
|
|
39
46
|
result = await doctorCommand(process.cwd(), option(args, "--repository"));
|
|
40
47
|
} else {
|
|
@@ -67,5 +74,10 @@ async function readJsonStdin() {
|
|
|
67
74
|
main().catch((error) => {
|
|
68
75
|
process.stderr.write(`wibe-bridge: ${error instanceof Error ? error.message : String(error)}
|
|
69
76
|
`);
|
|
70
|
-
process.
|
|
77
|
+
if (process.argv[2] === "emit") {
|
|
78
|
+
process.stdout.write("{}\n");
|
|
79
|
+
process.exitCode = 0;
|
|
80
|
+
} else {
|
|
81
|
+
process.exitCode = 1;
|
|
82
|
+
}
|
|
71
83
|
});
|
package/dist/codex-hook.js
CHANGED
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -5,10 +5,10 @@ Claude Code settings file. It uses command hooks because the local bridge must n
|
|
|
5
5
|
the payload before network delivery. Do not point an HTTP hook directly at a collector: Claude hook
|
|
6
6
|
payloads can include prompts, tool inputs/results, and other raw content.
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
messages, command text, tool arguments/results, and transcripts.
|
|
8
|
+
Run `wibe setup` to authorize a revocable, project-scoped device token. Interactive credentials are
|
|
9
|
+
stored in the operating-system keychain; hooks do not require repository secrets. The bridge
|
|
10
|
+
allow-list excludes prompt, content, messages, command text, tool arguments/results, and transcripts.
|
|
11
11
|
|
|
12
|
-
`mcp.json.example`
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
`mcp.json.example` connects Claude Code to Wibe's remote MCP endpoint at
|
|
13
|
+
`${env:WIBE_APP_URL}/api/mcp`. The MCP client completes OAuth separately from the bridge device token.
|
|
14
|
+
Set `WIBE_APP_URL` to the same Wibe origin used during setup.
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
# Codex template
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
These are reviewable setup templates. `config.toml.example` configures Codex notifications and MCP;
|
|
4
|
+
`hooks.json.example` provides lifecycle hooks where supported. Wibe installs native files only when
|
|
5
|
+
the destination does not already exist. The bridge maps allow-listed fields in-process and queues or
|
|
6
|
+
sends only canonical events. Prompt text, messages, tool data, command text, file content, and
|
|
7
|
+
transcripts are not retained.
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
Run `wibe setup` to authorize a revocable, project-scoped device token stored in the operating-system
|
|
10
|
+
keychain. Hooks do not require repository signing secrets.
|
|
10
11
|
|
|
11
|
-
The MCP block
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
The MCP block connects Codex to Wibe's remote `/api/mcp` endpoint. Codex completes OAuth separately
|
|
13
|
+
from the bridge device token. Set `WIBE_APP_URL` to the same Wibe origin used during setup. Codex
|
|
14
|
+
notification coverage can be narrower than Cursor or Claude Code lifecycle hooks.
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
# Cursor template
|
|
2
2
|
|
|
3
|
-
These files are
|
|
4
|
-
`hooks.json.example` into `.cursor/hooks.json
|
|
5
|
-
`npx
|
|
3
|
+
These files are reviewable setup templates. Wibe installs them only when the destination does not
|
|
4
|
+
already exist; otherwise merge `hooks.json.example` into `.cursor/hooks.json` yourself. Hook commands
|
|
5
|
+
run `npx -y @wibeco/bridge@latest emit`, which normalizes, redacts, queues, and delivers activity.
|
|
6
|
+
The emit command always returns a valid empty JSON object to Cursor and fails open, so telemetry
|
|
7
|
+
delivery can never block an agent tool call.
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
body, command text, tool arguments/results, and transcripts.
|
|
9
|
+
Run `wibe setup` to authorize a revocable, project-scoped device token. Interactive credentials are
|
|
10
|
+
stored in the operating-system keychain; hooks do not require repository secrets. The mapper drops
|
|
11
|
+
prompt, content, file body, command text, tool arguments/results, and transcripts.
|
|
10
12
|
|
|
11
|
-
`mcp.json.example`
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
`mcp.json.example` connects Cursor to Wibe's remote MCP endpoint at
|
|
14
|
+
`${env:WIBE_APP_URL}/api/mcp`. Cursor completes the endpoint's OAuth flow separately from the bridge
|
|
15
|
+
device token. Set `WIBE_APP_URL` to the same Wibe origin used during setup.
|
|
@@ -3,57 +3,68 @@
|
|
|
3
3
|
"hooks": {
|
|
4
4
|
"sessionStart": [
|
|
5
5
|
{
|
|
6
|
-
"command": "npx -y @wibeco/bridge@latest emit --adapter cursor --event session-start"
|
|
6
|
+
"command": "npx -y @wibeco/bridge@latest emit --adapter cursor --event session-start",
|
|
7
|
+
"failClosed": false
|
|
7
8
|
}
|
|
8
9
|
],
|
|
9
10
|
"sessionEnd": [
|
|
10
11
|
{
|
|
11
|
-
"command": "npx -y @wibeco/bridge@latest emit --adapter cursor --event session-end"
|
|
12
|
+
"command": "npx -y @wibeco/bridge@latest emit --adapter cursor --event session-end",
|
|
13
|
+
"failClosed": false
|
|
12
14
|
}
|
|
13
15
|
],
|
|
14
16
|
"beforeSubmitPrompt": [
|
|
15
17
|
{
|
|
16
|
-
"command": "npx -y @wibeco/bridge@latest emit --adapter cursor --event before-submit-prompt"
|
|
18
|
+
"command": "npx -y @wibeco/bridge@latest emit --adapter cursor --event before-submit-prompt",
|
|
19
|
+
"failClosed": false
|
|
17
20
|
}
|
|
18
21
|
],
|
|
19
22
|
"afterAgentResponse": [
|
|
20
23
|
{
|
|
21
|
-
"command": "npx -y @wibeco/bridge@latest emit --adapter cursor --event after-agent-response"
|
|
24
|
+
"command": "npx -y @wibeco/bridge@latest emit --adapter cursor --event after-agent-response",
|
|
25
|
+
"failClosed": false
|
|
22
26
|
}
|
|
23
27
|
],
|
|
24
28
|
"preToolUse": [
|
|
25
29
|
{
|
|
26
|
-
"command": "npx -y @wibeco/bridge@latest emit --adapter cursor --event before-tool"
|
|
30
|
+
"command": "npx -y @wibeco/bridge@latest emit --adapter cursor --event before-tool",
|
|
31
|
+
"failClosed": false
|
|
27
32
|
}
|
|
28
33
|
],
|
|
29
34
|
"postToolUse": [
|
|
30
35
|
{
|
|
31
|
-
"command": "npx -y @wibeco/bridge@latest emit --adapter cursor --event after-tool"
|
|
36
|
+
"command": "npx -y @wibeco/bridge@latest emit --adapter cursor --event after-tool",
|
|
37
|
+
"failClosed": false
|
|
32
38
|
}
|
|
33
39
|
],
|
|
34
40
|
"afterFileEdit": [
|
|
35
41
|
{
|
|
36
|
-
"command": "npx -y @wibeco/bridge@latest emit --adapter cursor --event after-file-edit"
|
|
42
|
+
"command": "npx -y @wibeco/bridge@latest emit --adapter cursor --event after-file-edit",
|
|
43
|
+
"failClosed": false
|
|
37
44
|
}
|
|
38
45
|
],
|
|
39
46
|
"beforeShellExecution": [
|
|
40
47
|
{
|
|
41
|
-
"command": "npx -y @wibeco/bridge@latest emit --adapter cursor --event before-shell-execution"
|
|
48
|
+
"command": "npx -y @wibeco/bridge@latest emit --adapter cursor --event before-shell-execution",
|
|
49
|
+
"failClosed": false
|
|
42
50
|
}
|
|
43
51
|
],
|
|
44
52
|
"afterShellExecution": [
|
|
45
53
|
{
|
|
46
|
-
"command": "npx -y @wibeco/bridge@latest emit --adapter cursor --event after-shell-execution"
|
|
54
|
+
"command": "npx -y @wibeco/bridge@latest emit --adapter cursor --event after-shell-execution",
|
|
55
|
+
"failClosed": false
|
|
47
56
|
}
|
|
48
57
|
],
|
|
49
58
|
"beforeMCPExecution": [
|
|
50
59
|
{
|
|
51
|
-
"command": "npx -y @wibeco/bridge@latest emit --adapter cursor --event before-mcp-execution"
|
|
60
|
+
"command": "npx -y @wibeco/bridge@latest emit --adapter cursor --event before-mcp-execution",
|
|
61
|
+
"failClosed": false
|
|
52
62
|
}
|
|
53
63
|
],
|
|
54
64
|
"afterMCPExecution": [
|
|
55
65
|
{
|
|
56
|
-
"command": "npx -y @wibeco/bridge@latest emit --adapter cursor --event after-mcp-execution"
|
|
66
|
+
"command": "npx -y @wibeco/bridge@latest emit --adapter cursor --event after-mcp-execution",
|
|
67
|
+
"failClosed": false
|
|
57
68
|
}
|
|
58
69
|
]
|
|
59
70
|
}
|