@wibeco/bridge 0.2.6 → 0.2.7
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.
|
@@ -15,7 +15,14 @@ var hookEventKindSchema = z.enum([
|
|
|
15
15
|
"shell.completed",
|
|
16
16
|
"mcp.started",
|
|
17
17
|
"mcp.completed",
|
|
18
|
+
"response.completed",
|
|
18
19
|
"progress.shared",
|
|
20
|
+
"git.branch_changed",
|
|
21
|
+
"git.commit_created",
|
|
22
|
+
"git.push_completed",
|
|
23
|
+
"git.merge_completed",
|
|
24
|
+
"git.rebase_completed",
|
|
25
|
+
"git.stash_created",
|
|
19
26
|
"unknown"
|
|
20
27
|
]);
|
|
21
28
|
var safeScalarSchema = z.union([z.string(), z.number(), z.boolean(), z.null()]);
|
|
@@ -156,7 +163,7 @@ var events = {
|
|
|
156
163
|
"pre-tool-use": "tool.started",
|
|
157
164
|
"post-tool-use": "tool.completed",
|
|
158
165
|
"post-tool-use-failure": "tool.completed",
|
|
159
|
-
notification: "
|
|
166
|
+
notification: "unknown"
|
|
160
167
|
};
|
|
161
168
|
var safeKeys = [
|
|
162
169
|
"hook_event_name",
|
|
@@ -171,12 +178,16 @@ var safeKeys = [
|
|
|
171
178
|
"session_started_at",
|
|
172
179
|
"heartbeat_at",
|
|
173
180
|
"heartbeat_interval_ms",
|
|
174
|
-
"session_elapsed_ms"
|
|
181
|
+
"session_elapsed_ms",
|
|
182
|
+
"title"
|
|
175
183
|
];
|
|
176
184
|
function mapClaudeCodeHook(eventName, input) {
|
|
177
185
|
const payload = objectPayload(input);
|
|
178
186
|
const toolInput = objectPayload(payload.tool_input);
|
|
179
187
|
const sessionId = safeIdentifier(payload.session_id);
|
|
188
|
+
const title = safeIdentifier(
|
|
189
|
+
payload.session_name ?? payload.title ?? payload.name
|
|
190
|
+
);
|
|
180
191
|
const model = safeModel(payload.model);
|
|
181
192
|
const path = safeRelativePath(
|
|
182
193
|
toolInput.file_path ?? payload.file_path,
|
|
@@ -191,6 +202,7 @@ function mapClaudeCodeHook(eventName, input) {
|
|
|
191
202
|
{
|
|
192
203
|
...payload,
|
|
193
204
|
...sessionId ? { session_id: sessionId } : {},
|
|
205
|
+
...title ? { title } : {},
|
|
194
206
|
...model ? { model } : {},
|
|
195
207
|
...path ? { path } : {},
|
|
196
208
|
...duration !== void 0 ? { duration_ms: duration } : {},
|
|
@@ -220,8 +232,8 @@ var events2 = {
|
|
|
220
232
|
"user-prompt-submit": "lifecycle.before",
|
|
221
233
|
"pre-tool-use": "tool.started",
|
|
222
234
|
"post-tool-use": "tool.completed",
|
|
223
|
-
"subagent-start": "
|
|
224
|
-
"subagent-stop": "
|
|
235
|
+
"subagent-start": "unknown",
|
|
236
|
+
"subagent-stop": "unknown",
|
|
225
237
|
stop: "lifecycle.after"
|
|
226
238
|
};
|
|
227
239
|
var safeKeys2 = [
|
|
@@ -237,7 +249,8 @@ var safeKeys2 = [
|
|
|
237
249
|
"session_started_at",
|
|
238
250
|
"heartbeat_at",
|
|
239
251
|
"heartbeat_interval_ms",
|
|
240
|
-
"session_elapsed_ms"
|
|
252
|
+
"session_elapsed_ms",
|
|
253
|
+
"title"
|
|
241
254
|
];
|
|
242
255
|
function mapCodexHook(eventName, input) {
|
|
243
256
|
const payload = objectPayload(input);
|
|
@@ -245,6 +258,9 @@ function mapCodexHook(eventName, input) {
|
|
|
245
258
|
const threadId = safeIdentifier(
|
|
246
259
|
payload["thread-id"] ?? payload.thread_id ?? payload.session_id
|
|
247
260
|
);
|
|
261
|
+
const title = safeIdentifier(
|
|
262
|
+
payload["thread-name"] ?? payload.thread_name ?? payload.title
|
|
263
|
+
);
|
|
248
264
|
const turnId = safeIdentifier(payload["turn-id"] ?? payload.turn_id);
|
|
249
265
|
const model = safeModel(payload.model);
|
|
250
266
|
const path = safeRelativePath(
|
|
@@ -257,6 +273,7 @@ function mapCodexHook(eventName, input) {
|
|
|
257
273
|
{
|
|
258
274
|
...payload,
|
|
259
275
|
...threadId ? { session_id: threadId } : {},
|
|
276
|
+
...title ? { title } : {},
|
|
260
277
|
...turnId ? { turn_id: turnId } : {},
|
|
261
278
|
...model ? { model } : {},
|
|
262
279
|
...path ? { path } : {},
|
|
@@ -296,11 +313,15 @@ var safeKeys3 = [
|
|
|
296
313
|
"session_started_at",
|
|
297
314
|
"heartbeat_at",
|
|
298
315
|
"heartbeat_interval_ms",
|
|
299
|
-
"session_elapsed_ms"
|
|
316
|
+
"session_elapsed_ms",
|
|
317
|
+
"title"
|
|
300
318
|
];
|
|
301
319
|
function mapCursorHook(eventName, input) {
|
|
302
320
|
const payload = objectPayload(input);
|
|
303
321
|
const sessionId = safeIdentifier(payload.conversation_id ?? payload.session_id);
|
|
322
|
+
const title = safeIdentifier(
|
|
323
|
+
payload.conversation_name ?? payload.title ?? payload.name
|
|
324
|
+
);
|
|
304
325
|
const model = safeModel(payload.model);
|
|
305
326
|
const workspaceRoot = Array.isArray(payload.workspace_roots) ? payload.workspace_roots.find(
|
|
306
327
|
(root) => typeof root === "string"
|
|
@@ -315,6 +336,7 @@ function mapCursorHook(eventName, input) {
|
|
|
315
336
|
{
|
|
316
337
|
...payload,
|
|
317
338
|
...sessionId ? { session_id: sessionId } : {},
|
|
339
|
+
...title ? { title } : {},
|
|
318
340
|
...model ? { model } : {},
|
|
319
341
|
...path ? { path } : {},
|
|
320
342
|
...typeof payload.lines_added === "number" ? { lines_added: payload.lines_added } : {},
|
|
@@ -332,7 +354,11 @@ function eventTypeForHook(event) {
|
|
|
332
354
|
if (event.kind === "presence.heartbeat") return "presence.heartbeat";
|
|
333
355
|
if (event.kind === "session.ended") return "presence.stopped";
|
|
334
356
|
if (event.kind === "file.changed") return "workspace.files_changed";
|
|
357
|
+
if (event.kind === "response.completed") return "agent.response_completed";
|
|
335
358
|
if (event.kind === "progress.shared") return "agent.progress_shared";
|
|
359
|
+
if (event.kind === "git.branch_changed" || event.kind === "git.commit_created" || event.kind === "git.push_completed" || event.kind === "git.merge_completed" || event.kind === "git.rebase_completed" || event.kind === "git.stash_created") {
|
|
360
|
+
return event.kind;
|
|
361
|
+
}
|
|
336
362
|
if (event.kind === "shell.completed" && event.outcome && isTestHookEvent(event)) {
|
|
337
363
|
return "workspace.test_completed";
|
|
338
364
|
}
|
|
@@ -373,6 +399,14 @@ function toEnvelope(event, options) {
|
|
|
373
399
|
task_lines_added: typeof event.metadata.task_lines_added === "number" ? event.metadata.task_lines_added : void 0,
|
|
374
400
|
task_lines_deleted: typeof event.metadata.task_lines_deleted === "number" ? event.metadata.task_lines_deleted : void 0,
|
|
375
401
|
task_paths: Array.isArray(event.metadata.task_paths) ? event.metadata.task_paths : void 0
|
|
402
|
+
} : event.kind === "response.completed" ? {
|
|
403
|
+
title: event.metadata.title,
|
|
404
|
+
paths,
|
|
405
|
+
lines_added: event.metadata.lines_added,
|
|
406
|
+
lines_deleted: event.metadata.lines_deleted,
|
|
407
|
+
tool: event.source,
|
|
408
|
+
agent_name: event.source,
|
|
409
|
+
hook_kind: event.kind
|
|
376
410
|
} : event.kind === "presence.heartbeat" ? {
|
|
377
411
|
verification: event.metadata.verification,
|
|
378
412
|
sequence: event.metadata.sequence,
|
|
@@ -396,6 +430,21 @@ function toEnvelope(event, options) {
|
|
|
396
430
|
tool: event.source,
|
|
397
431
|
agent_name: event.source,
|
|
398
432
|
hook_kind: event.kind
|
|
433
|
+
} : event.kind.startsWith("git.") ? {
|
|
434
|
+
title: event.metadata.title,
|
|
435
|
+
previous_branch: event.metadata.previous_branch,
|
|
436
|
+
branch: event.repo?.branch,
|
|
437
|
+
previous_commit: event.metadata.previous_commit,
|
|
438
|
+
commit_sha: event.metadata.commit_sha ?? event.repo?.commit,
|
|
439
|
+
stash_sha: event.metadata.stash_sha,
|
|
440
|
+
paths_known: event.metadata.paths_known,
|
|
441
|
+
paths,
|
|
442
|
+
changes: event.metadata.changes,
|
|
443
|
+
lines_added: event.metadata.lines_added,
|
|
444
|
+
lines_deleted: event.metadata.lines_deleted,
|
|
445
|
+
tool: event.source,
|
|
446
|
+
agent_name: event.source,
|
|
447
|
+
hook_kind: event.kind
|
|
399
448
|
} : {
|
|
400
449
|
paths,
|
|
401
450
|
tool: event.source,
|
|
@@ -416,7 +465,7 @@ function toEnvelope(event, options) {
|
|
|
416
465
|
source: "local_collector",
|
|
417
466
|
type,
|
|
418
467
|
visibility: "project",
|
|
419
|
-
idempotency_key: `${event.source}:${event.id}`,
|
|
468
|
+
idempotency_key: gitIdempotencyKey(event, options) ?? `${event.source}:${event.id}`,
|
|
420
469
|
correlation: {
|
|
421
470
|
session_id: event.sessionId,
|
|
422
471
|
branch: event.repo?.branch,
|
|
@@ -428,6 +477,14 @@ function toEnvelope(event, options) {
|
|
|
428
477
|
payload
|
|
429
478
|
};
|
|
430
479
|
}
|
|
480
|
+
function gitIdempotencyKey(event, options) {
|
|
481
|
+
if (!event.kind.startsWith("git.")) return void 0;
|
|
482
|
+
const identity = event.kind === "git.stash_created" ? event.metadata.stash_sha : event.metadata.commit_sha ?? event.repo?.commit;
|
|
483
|
+
if (typeof identity !== "string" || !identity) return void 0;
|
|
484
|
+
const repository = options.repositoryId ?? "repository";
|
|
485
|
+
const branch = event.repo?.branch ?? "detached";
|
|
486
|
+
return `git:${event.kind.slice(4)}:${repository}:${branch}:${identity}`;
|
|
487
|
+
}
|
|
431
488
|
var SignedBatchClient = class {
|
|
432
489
|
constructor(options) {
|
|
433
490
|
this.options = options;
|
|
@@ -708,8 +765,16 @@ async function updatePresenceSession(source, sessionId, event, cwd = process.cwd
|
|
|
708
765
|
if (event.outcome === "success") state.testsPassed += 1;
|
|
709
766
|
if (event.outcome === "failure") state.testsFailed += 1;
|
|
710
767
|
}
|
|
768
|
+
const metrics = metricsFromState(state, measuredAt);
|
|
769
|
+
if (event.kind === "lifecycle.after") {
|
|
770
|
+
state.taskBaselineLinesAdded = workingLinesAdded;
|
|
771
|
+
state.taskBaselineLinesDeleted = workingLinesDeleted;
|
|
772
|
+
state.taskLinesAdded = 0;
|
|
773
|
+
state.taskLinesDeleted = 0;
|
|
774
|
+
state.taskPaths = [];
|
|
775
|
+
}
|
|
711
776
|
await writePresenceState(statePath, state);
|
|
712
|
-
return
|
|
777
|
+
return metrics;
|
|
713
778
|
}
|
|
714
779
|
async function stopPresenceSession(source, sessionId, cwd = process.cwd()) {
|
|
715
780
|
const statePath = presenceStatePath(source, sessionId, cwd);
|
|
@@ -889,16 +954,34 @@ function delay(milliseconds) {
|
|
|
889
954
|
}
|
|
890
955
|
|
|
891
956
|
// src/repo.ts
|
|
957
|
+
import { createHash as createHash2 } from "crypto";
|
|
892
958
|
import { execFile } from "child_process";
|
|
959
|
+
import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
960
|
+
import { homedir as homedir2 } from "os";
|
|
961
|
+
import { join as join2 } from "path";
|
|
893
962
|
import { promisify } from "util";
|
|
894
963
|
var execFileAsync = promisify(execFile);
|
|
964
|
+
function classifyHeadTransition(reflogAction, headParentCount) {
|
|
965
|
+
const reflog = reflogAction?.toLowerCase() ?? "";
|
|
966
|
+
if (reflog.startsWith("rebase")) return "git.rebase_completed";
|
|
967
|
+
if (reflog.startsWith("merge") && headParentCount && headParentCount > 1) {
|
|
968
|
+
return "git.merge_completed";
|
|
969
|
+
}
|
|
970
|
+
return reflog.startsWith("commit") ? "git.commit_created" : void 0;
|
|
971
|
+
}
|
|
972
|
+
function isObservedPush(previous, current, branchChanged = false) {
|
|
973
|
+
const reflog = current.reflogAction?.toLowerCase() ?? "";
|
|
974
|
+
return Boolean(
|
|
975
|
+
!branchChanged && !reflog.startsWith("pull") && !reflog.startsWith("fetch") && current.commit && current.upstreamCommit === current.commit && previous.upstreamCommit !== current.upstreamCommit
|
|
976
|
+
);
|
|
977
|
+
}
|
|
895
978
|
async function git(cwd, args) {
|
|
896
979
|
try {
|
|
897
980
|
const { stdout } = await execFileAsync("git", args, {
|
|
898
981
|
cwd,
|
|
899
982
|
encoding: "utf8",
|
|
900
983
|
timeout: 2e3,
|
|
901
|
-
maxBuffer:
|
|
984
|
+
maxBuffer: 4 * 1024 * 1024
|
|
902
985
|
});
|
|
903
986
|
return stdout.trim() || void 0;
|
|
904
987
|
} catch {
|
|
@@ -929,10 +1012,24 @@ async function detectDefaultBranch(root) {
|
|
|
929
1012
|
async function detectRepository(cwd = process.cwd()) {
|
|
930
1013
|
const root = await git(cwd, ["rev-parse", "--show-toplevel"]);
|
|
931
1014
|
if (!root) return void 0;
|
|
932
|
-
const [
|
|
1015
|
+
const [
|
|
1016
|
+
remote,
|
|
1017
|
+
branch,
|
|
1018
|
+
commit,
|
|
1019
|
+
upstreamCommit,
|
|
1020
|
+
stashCommit,
|
|
1021
|
+
parents,
|
|
1022
|
+
reflogAction,
|
|
1023
|
+
commitTitle
|
|
1024
|
+
] = await Promise.all([
|
|
933
1025
|
git(root, ["remote", "get-url", "origin"]),
|
|
934
1026
|
git(root, ["branch", "--show-current"]),
|
|
935
|
-
git(root, ["rev-parse", "HEAD"])
|
|
1027
|
+
git(root, ["rev-parse", "HEAD"]),
|
|
1028
|
+
git(root, ["rev-parse", "@{upstream}"]),
|
|
1029
|
+
git(root, ["rev-parse", "refs/stash"]),
|
|
1030
|
+
git(root, ["rev-list", "--parents", "-n", "1", "HEAD"]),
|
|
1031
|
+
git(root, ["reflog", "-1", "--format=%gs"]),
|
|
1032
|
+
git(root, ["log", "-1", "--format=%s"])
|
|
936
1033
|
]);
|
|
937
1034
|
const defaultBranch = await detectDefaultBranch(root);
|
|
938
1035
|
const baseCommit = branch && commit && defaultBranch ? branch === defaultBranch.branch ? commit : await git(root, [
|
|
@@ -953,7 +1050,144 @@ async function detectRepository(cwd = process.cwd()) {
|
|
|
953
1050
|
...commit ? { commit } : {},
|
|
954
1051
|
...defaultBranch ? { defaultBranch: defaultBranch.branch } : {},
|
|
955
1052
|
...baseCommit ? { baseCommit } : {},
|
|
956
|
-
...behindBy !== void 0 ? { behindBy } : {}
|
|
1053
|
+
...behindBy !== void 0 ? { behindBy } : {},
|
|
1054
|
+
...upstreamCommit ? { upstreamCommit } : {},
|
|
1055
|
+
...stashCommit ? { stashCommit } : {},
|
|
1056
|
+
...parents ? { headParentCount: Math.max(0, parents.split(/\s+/).length - 1) } : {},
|
|
1057
|
+
...reflogAction ? { reflogAction } : {},
|
|
1058
|
+
...commitTitle ? { commitTitle } : {}
|
|
1059
|
+
};
|
|
1060
|
+
}
|
|
1061
|
+
async function observeRepositoryTransitions(source, sessionId, current) {
|
|
1062
|
+
const directory = process.env.WIBE_GIT_STATE_DIR ?? join2(homedir2(), ".wibe", "git-state");
|
|
1063
|
+
const key = createHash2("sha256").update(`${source}\0${sessionId ?? ""}\0${current.root}`).digest("hex");
|
|
1064
|
+
const statePath = join2(directory, `${key}.json`);
|
|
1065
|
+
let previous;
|
|
1066
|
+
try {
|
|
1067
|
+
previous = JSON.parse(await readFile3(statePath, "utf8"));
|
|
1068
|
+
} catch {
|
|
1069
|
+
previous = void 0;
|
|
1070
|
+
}
|
|
1071
|
+
await mkdir3(directory, { recursive: true, mode: 448 });
|
|
1072
|
+
await writeFile3(statePath, `${JSON.stringify(current)}
|
|
1073
|
+
`, { mode: 384 });
|
|
1074
|
+
if (!previous) return [];
|
|
1075
|
+
const transitions = [];
|
|
1076
|
+
const branchChanged = Boolean(current.branch) && Boolean(previous.branch) && current.branch !== previous.branch;
|
|
1077
|
+
if (branchChanged) {
|
|
1078
|
+
transitions.push({
|
|
1079
|
+
kind: "git.branch_changed",
|
|
1080
|
+
metadata: {
|
|
1081
|
+
title: "Changed branch",
|
|
1082
|
+
previous_branch: previous.branch,
|
|
1083
|
+
branch: current.branch
|
|
1084
|
+
}
|
|
1085
|
+
});
|
|
1086
|
+
}
|
|
1087
|
+
if (current.stashCommit && current.stashCommit !== previous.stashCommit) {
|
|
1088
|
+
transitions.push({
|
|
1089
|
+
kind: "git.stash_created",
|
|
1090
|
+
metadata: {
|
|
1091
|
+
title: "Stashed changes",
|
|
1092
|
+
stash_sha: current.stashCommit
|
|
1093
|
+
}
|
|
1094
|
+
});
|
|
1095
|
+
}
|
|
1096
|
+
if (!branchChanged && current.commit && previous.commit && current.commit !== previous.commit) {
|
|
1097
|
+
const change = await detectRevisionChanges(
|
|
1098
|
+
current.root,
|
|
1099
|
+
previous.commit,
|
|
1100
|
+
current.commit
|
|
1101
|
+
);
|
|
1102
|
+
const kind = classifyHeadTransition(
|
|
1103
|
+
current.reflogAction,
|
|
1104
|
+
current.headParentCount
|
|
1105
|
+
);
|
|
1106
|
+
if (kind) {
|
|
1107
|
+
transitions.push({
|
|
1108
|
+
kind,
|
|
1109
|
+
metadata: {
|
|
1110
|
+
title: kind === "git.rebase_completed" ? "Rebased" : kind === "git.merge_completed" ? "Merged" : current.commitTitle || "Committed",
|
|
1111
|
+
previous_commit: previous.commit,
|
|
1112
|
+
commit_sha: current.commit,
|
|
1113
|
+
paths_known: change.pathsKnown,
|
|
1114
|
+
paths: change.paths,
|
|
1115
|
+
changes: change.changes,
|
|
1116
|
+
lines_added: change.linesAdded,
|
|
1117
|
+
lines_deleted: change.linesDeleted
|
|
1118
|
+
}
|
|
1119
|
+
});
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
if (isObservedPush(previous, current, branchChanged) && current.commit) {
|
|
1123
|
+
const change = previous.upstreamCommit ? await detectRevisionChanges(
|
|
1124
|
+
current.root,
|
|
1125
|
+
previous.upstreamCommit,
|
|
1126
|
+
current.commit
|
|
1127
|
+
) : {
|
|
1128
|
+
pathsKnown: false,
|
|
1129
|
+
paths: [],
|
|
1130
|
+
changes: [],
|
|
1131
|
+
linesAdded: 0,
|
|
1132
|
+
linesDeleted: 0
|
|
1133
|
+
};
|
|
1134
|
+
transitions.push({
|
|
1135
|
+
kind: "git.push_completed",
|
|
1136
|
+
metadata: {
|
|
1137
|
+
title: `Pushed to ${current.branch ?? "remote"}`,
|
|
1138
|
+
commit_sha: current.commit,
|
|
1139
|
+
paths_known: change.pathsKnown,
|
|
1140
|
+
paths: change.paths,
|
|
1141
|
+
changes: change.changes,
|
|
1142
|
+
lines_added: change.linesAdded,
|
|
1143
|
+
lines_deleted: change.linesDeleted
|
|
1144
|
+
}
|
|
1145
|
+
});
|
|
1146
|
+
}
|
|
1147
|
+
return transitions;
|
|
1148
|
+
}
|
|
1149
|
+
async function detectRevisionChanges(root, previous, current) {
|
|
1150
|
+
const [numstat, nameStatus] = await Promise.all([
|
|
1151
|
+
git(root, ["diff", "--numstat", previous, current, "--"]),
|
|
1152
|
+
git(root, ["diff", "--name-status", previous, current, "--"])
|
|
1153
|
+
]);
|
|
1154
|
+
const pathsKnown = numstat !== void 0 && nameStatus !== void 0;
|
|
1155
|
+
const statusByPath = /* @__PURE__ */ new Map();
|
|
1156
|
+
for (const line of nameStatus?.split("\n") ?? []) {
|
|
1157
|
+
if (!line) continue;
|
|
1158
|
+
const [status, ...pathParts] = line.split(" ");
|
|
1159
|
+
const path = pathParts.at(-1);
|
|
1160
|
+
if (!path || isSensitivePath(path)) continue;
|
|
1161
|
+
statusByPath.set(
|
|
1162
|
+
path,
|
|
1163
|
+
status?.startsWith("A") ? "added" : status?.startsWith("D") ? "deleted" : "modified"
|
|
1164
|
+
);
|
|
1165
|
+
}
|
|
1166
|
+
let linesAdded = 0;
|
|
1167
|
+
let linesDeleted = 0;
|
|
1168
|
+
const changes = [];
|
|
1169
|
+
for (const line of numstat?.split("\n") ?? []) {
|
|
1170
|
+
if (!line) continue;
|
|
1171
|
+
const [added, deleted, ...pathParts] = line.split(" ");
|
|
1172
|
+
const path = pathParts.join(" ");
|
|
1173
|
+
if (!path || isSensitivePath(path)) continue;
|
|
1174
|
+
const additions = /^\d+$/.test(added) ? Number(added) : 0;
|
|
1175
|
+
const deletions = /^\d+$/.test(deleted) ? Number(deleted) : 0;
|
|
1176
|
+
linesAdded += additions;
|
|
1177
|
+
linesDeleted += deletions;
|
|
1178
|
+
changes.push({
|
|
1179
|
+
path,
|
|
1180
|
+
change_type: statusByPath.get(path) ?? "modified",
|
|
1181
|
+
additions,
|
|
1182
|
+
deletions
|
|
1183
|
+
});
|
|
1184
|
+
}
|
|
1185
|
+
return {
|
|
1186
|
+
pathsKnown,
|
|
1187
|
+
paths: changes.map((change) => change.path).slice(0, 2e3),
|
|
1188
|
+
changes: changes.slice(0, 2e3),
|
|
1189
|
+
linesAdded,
|
|
1190
|
+
linesDeleted
|
|
957
1191
|
};
|
|
958
1192
|
}
|
|
959
1193
|
async function detectWorkingTreeMetrics(root) {
|
|
@@ -1181,7 +1415,10 @@ export {
|
|
|
1181
1415
|
stopPresenceSession,
|
|
1182
1416
|
runPresenceHeartbeat,
|
|
1183
1417
|
presenceStatePath,
|
|
1418
|
+
classifyHeadTransition,
|
|
1419
|
+
isObservedPush,
|
|
1184
1420
|
detectRepository,
|
|
1421
|
+
observeRepositoryTransitions,
|
|
1185
1422
|
detectWorkingTreeMetrics,
|
|
1186
1423
|
sanitizeRemote,
|
|
1187
1424
|
normalizeGitHubRepository,
|
|
@@ -12,12 +12,13 @@ import {
|
|
|
12
12
|
mapCursorHook,
|
|
13
13
|
matchesGitHubRepository,
|
|
14
14
|
normalizeGitHubRepository,
|
|
15
|
+
observeRepositoryTransitions,
|
|
15
16
|
pollDeviceToken,
|
|
16
17
|
requestDeviceAuthorization,
|
|
17
18
|
startPresenceSession,
|
|
18
19
|
stopPresenceSession,
|
|
19
20
|
updatePresenceSession
|
|
20
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-O4MHMJZZ.js";
|
|
21
22
|
|
|
22
23
|
// src/cli/commands.ts
|
|
23
24
|
import { access, cp, mkdir, readFile, writeFile } from "fs/promises";
|
|
@@ -257,6 +258,18 @@ async function emitCommand(adapter, eventName, input) {
|
|
|
257
258
|
task_paths: metrics.task_paths
|
|
258
259
|
}
|
|
259
260
|
};
|
|
261
|
+
} else if (mappedEvent.kind === "lifecycle.after" && metrics && metrics.task_paths.length > 0) {
|
|
262
|
+
mappedEvent = {
|
|
263
|
+
...mappedEvent,
|
|
264
|
+
kind: "response.completed",
|
|
265
|
+
metadata: {
|
|
266
|
+
...mappedEvent.metadata,
|
|
267
|
+
title: "Agent update",
|
|
268
|
+
paths: metrics.task_paths,
|
|
269
|
+
lines_added: metrics.task_lines_added,
|
|
270
|
+
lines_deleted: metrics.task_lines_deleted
|
|
271
|
+
}
|
|
272
|
+
};
|
|
260
273
|
} else if (mappedEvent.kind === "presence.heartbeat" && metrics) {
|
|
261
274
|
const publicMetadata = { ...mappedEvent.metadata };
|
|
262
275
|
delete publicMetadata.working_tree_paths;
|
|
@@ -274,16 +287,32 @@ async function emitCommand(adapter, eventName, input) {
|
|
|
274
287
|
mappedEvent = { ...mappedEvent, metadata: publicMetadata };
|
|
275
288
|
}
|
|
276
289
|
const event = repo ? { ...mappedEvent, repo } : mappedEvent;
|
|
290
|
+
const repositoryEvents = repo ? (await observeRepositoryTransitions(
|
|
291
|
+
adapter,
|
|
292
|
+
mappedEvent.sessionId,
|
|
293
|
+
repo
|
|
294
|
+
)).map(
|
|
295
|
+
(transition) => createHookEvent({
|
|
296
|
+
source: adapter,
|
|
297
|
+
kind: transition.kind,
|
|
298
|
+
...mappedEvent.sessionId ? { sessionId: mappedEvent.sessionId } : {},
|
|
299
|
+
repo,
|
|
300
|
+
metadata: transition.metadata
|
|
301
|
+
})
|
|
302
|
+
) : [];
|
|
303
|
+
const publishableEvents = [event, ...repositoryEvents].filter(
|
|
304
|
+
(candidate) => eventTypeForHook(candidate)
|
|
305
|
+
);
|
|
277
306
|
const queue = new JsonFileOfflineQueue(queuePath());
|
|
278
307
|
const credential = await loadCredential(process.cwd());
|
|
279
308
|
if (!credential) {
|
|
280
|
-
if (
|
|
309
|
+
if (publishableEvents.length) await queue.enqueue(publishableEvents);
|
|
281
310
|
return {
|
|
282
311
|
exitCode: 0,
|
|
283
|
-
message:
|
|
312
|
+
message: publishableEvents.length ? `Queued ${publishableEvents.length} event(s); this repository is not connected to Wibe.` : `Ignored operational ${event.kind} hook.`
|
|
284
313
|
};
|
|
285
314
|
}
|
|
286
|
-
const
|
|
315
|
+
const client = new SignedBatchClient({
|
|
287
316
|
endpoint: `${credential.appUrl}/api/events/batch`,
|
|
288
317
|
accessToken: credential.accessToken,
|
|
289
318
|
organizationId: credential.organizationId,
|
|
@@ -291,10 +320,19 @@ async function emitCommand(adapter, eventName, input) {
|
|
|
291
320
|
repositoryId: credential.repositoryId,
|
|
292
321
|
deviceId: credential.deviceId,
|
|
293
322
|
queue
|
|
294
|
-
})
|
|
323
|
+
});
|
|
324
|
+
let sent = 0;
|
|
325
|
+
let remaining = await queue.size();
|
|
326
|
+
let deliveryError;
|
|
327
|
+
for (const candidate of publishableEvents) {
|
|
328
|
+
const result = await client.capture(candidate);
|
|
329
|
+
sent += result.sent;
|
|
330
|
+
remaining = result.remaining;
|
|
331
|
+
deliveryError ??= result.error;
|
|
332
|
+
}
|
|
295
333
|
return {
|
|
296
|
-
exitCode:
|
|
297
|
-
message:
|
|
334
|
+
exitCode: deliveryError ? 1 : 0,
|
|
335
|
+
message: deliveryError ? `Queued ${publishableEvents.length} event(s); delivery failed: ${deliveryError}` : publishableEvents.length ? `Delivered ${sent} event(s); ${remaining} queued.` : `Ignored operational ${event.kind} hook.`
|
|
298
336
|
};
|
|
299
337
|
}
|
|
300
338
|
async function shareProgressCommand(options, cwd = process.cwd()) {
|
package/dist/cli.js
CHANGED
|
@@ -6,10 +6,10 @@ import {
|
|
|
6
6
|
setupCommand,
|
|
7
7
|
shareProgressCommand,
|
|
8
8
|
statusCommand
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-VBQSVC34.js";
|
|
10
10
|
import {
|
|
11
11
|
runPresenceHeartbeat
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-O4MHMJZZ.js";
|
|
13
13
|
|
|
14
14
|
// src/cli.ts
|
|
15
15
|
var HELP = `wibe-bridge <command>
|
package/dist/codex-hook.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -19,7 +19,14 @@ declare const hookEventKindSchema: z.ZodEnum<{
|
|
|
19
19
|
"shell.completed": "shell.completed";
|
|
20
20
|
"mcp.started": "mcp.started";
|
|
21
21
|
"mcp.completed": "mcp.completed";
|
|
22
|
+
"response.completed": "response.completed";
|
|
22
23
|
"progress.shared": "progress.shared";
|
|
24
|
+
"git.branch_changed": "git.branch_changed";
|
|
25
|
+
"git.commit_created": "git.commit_created";
|
|
26
|
+
"git.push_completed": "git.push_completed";
|
|
27
|
+
"git.merge_completed": "git.merge_completed";
|
|
28
|
+
"git.rebase_completed": "git.rebase_completed";
|
|
29
|
+
"git.stash_created": "git.stash_created";
|
|
23
30
|
unknown: "unknown";
|
|
24
31
|
}>;
|
|
25
32
|
type HookEventKind = z.infer<typeof hookEventKindSchema>;
|
|
@@ -49,7 +56,14 @@ declare const canonicalHookEventSchema: z.ZodObject<{
|
|
|
49
56
|
"shell.completed": "shell.completed";
|
|
50
57
|
"mcp.started": "mcp.started";
|
|
51
58
|
"mcp.completed": "mcp.completed";
|
|
59
|
+
"response.completed": "response.completed";
|
|
52
60
|
"progress.shared": "progress.shared";
|
|
61
|
+
"git.branch_changed": "git.branch_changed";
|
|
62
|
+
"git.commit_created": "git.commit_created";
|
|
63
|
+
"git.push_completed": "git.push_completed";
|
|
64
|
+
"git.merge_completed": "git.merge_completed";
|
|
65
|
+
"git.rebase_completed": "git.rebase_completed";
|
|
66
|
+
"git.stash_created": "git.stash_created";
|
|
53
67
|
unknown: "unknown";
|
|
54
68
|
}>;
|
|
55
69
|
occurredAt: z.ZodString;
|
|
@@ -79,7 +93,7 @@ declare function mapClaudeCodeHook(eventName: string, input: unknown): {
|
|
|
79
93
|
id: string;
|
|
80
94
|
version: 1;
|
|
81
95
|
source: "cursor" | "claude-code" | "codex";
|
|
82
|
-
kind: "presence.heartbeat" | "session.started" | "session.ended" | "lifecycle.before" | "lifecycle.after" | "tool.started" | "tool.completed" | "file.changed" | "shell.started" | "shell.completed" | "mcp.started" | "mcp.completed" | "progress.shared" | "unknown";
|
|
96
|
+
kind: "presence.heartbeat" | "session.started" | "session.ended" | "lifecycle.before" | "lifecycle.after" | "tool.started" | "tool.completed" | "file.changed" | "shell.started" | "shell.completed" | "mcp.started" | "mcp.completed" | "response.completed" | "progress.shared" | "git.branch_changed" | "git.commit_created" | "git.push_completed" | "git.merge_completed" | "git.rebase_completed" | "git.stash_created" | "unknown";
|
|
83
97
|
occurredAt: string;
|
|
84
98
|
metadata: Record<string, SafeValue>;
|
|
85
99
|
sessionId?: string | undefined;
|
|
@@ -100,7 +114,7 @@ declare function mapCodexHook(eventName: string, input: unknown): {
|
|
|
100
114
|
id: string;
|
|
101
115
|
version: 1;
|
|
102
116
|
source: "cursor" | "claude-code" | "codex";
|
|
103
|
-
kind: "presence.heartbeat" | "session.started" | "session.ended" | "lifecycle.before" | "lifecycle.after" | "tool.started" | "tool.completed" | "file.changed" | "shell.started" | "shell.completed" | "mcp.started" | "mcp.completed" | "progress.shared" | "unknown";
|
|
117
|
+
kind: "presence.heartbeat" | "session.started" | "session.ended" | "lifecycle.before" | "lifecycle.after" | "tool.started" | "tool.completed" | "file.changed" | "shell.started" | "shell.completed" | "mcp.started" | "mcp.completed" | "response.completed" | "progress.shared" | "git.branch_changed" | "git.commit_created" | "git.push_completed" | "git.merge_completed" | "git.rebase_completed" | "git.stash_created" | "unknown";
|
|
104
118
|
occurredAt: string;
|
|
105
119
|
metadata: Record<string, SafeValue>;
|
|
106
120
|
sessionId?: string | undefined;
|
|
@@ -121,7 +135,7 @@ declare function mapCursorHook(eventName: string, input: unknown): {
|
|
|
121
135
|
id: string;
|
|
122
136
|
version: 1;
|
|
123
137
|
source: "cursor" | "claude-code" | "codex";
|
|
124
|
-
kind: "presence.heartbeat" | "session.started" | "session.ended" | "lifecycle.before" | "lifecycle.after" | "tool.started" | "tool.completed" | "file.changed" | "shell.started" | "shell.completed" | "mcp.started" | "mcp.completed" | "progress.shared" | "unknown";
|
|
138
|
+
kind: "presence.heartbeat" | "session.started" | "session.ended" | "lifecycle.before" | "lifecycle.after" | "tool.started" | "tool.completed" | "file.changed" | "shell.started" | "shell.completed" | "mcp.started" | "mcp.completed" | "response.completed" | "progress.shared" | "git.branch_changed" | "git.commit_created" | "git.push_completed" | "git.merge_completed" | "git.rebase_completed" | "git.stash_created" | "unknown";
|
|
125
139
|
occurredAt: string;
|
|
126
140
|
metadata: Record<string, SafeValue>;
|
|
127
141
|
sessionId?: string | undefined;
|
|
@@ -251,7 +265,7 @@ interface SignedBatchClientOptions {
|
|
|
251
265
|
batchSize?: number;
|
|
252
266
|
timeoutMs?: number;
|
|
253
267
|
}
|
|
254
|
-
declare function eventTypeForHook(event: CanonicalHookEvent): "presence.heartbeat" | "presence.started" | "presence.stopped" | "workspace.files_changed" | "agent.progress_shared" | "workspace.test_completed" | null;
|
|
268
|
+
declare function eventTypeForHook(event: CanonicalHookEvent): "presence.heartbeat" | "git.branch_changed" | "git.commit_created" | "git.push_completed" | "git.merge_completed" | "git.rebase_completed" | "git.stash_created" | "presence.started" | "presence.stopped" | "workspace.files_changed" | "agent.response_completed" | "agent.progress_shared" | "workspace.test_completed" | null;
|
|
255
269
|
interface FlushResult {
|
|
256
270
|
sent: number;
|
|
257
271
|
remaining: number;
|
|
@@ -307,16 +321,28 @@ interface RepositoryInfo {
|
|
|
307
321
|
defaultBranch?: string;
|
|
308
322
|
baseCommit?: string;
|
|
309
323
|
behindBy?: number;
|
|
324
|
+
upstreamCommit?: string;
|
|
325
|
+
stashCommit?: string;
|
|
326
|
+
headParentCount?: number;
|
|
327
|
+
reflogAction?: string;
|
|
328
|
+
commitTitle?: string;
|
|
310
329
|
}
|
|
311
330
|
interface WorkingTreeMetrics {
|
|
312
331
|
linesAdded: number;
|
|
313
332
|
linesDeleted: number;
|
|
314
333
|
paths: string[];
|
|
315
334
|
}
|
|
335
|
+
interface RepositoryTransition {
|
|
336
|
+
kind: Extract<HookEventKind, "git.branch_changed" | "git.commit_created" | "git.push_completed" | "git.merge_completed" | "git.rebase_completed" | "git.stash_created">;
|
|
337
|
+
metadata: Record<string, SafeValue>;
|
|
338
|
+
}
|
|
339
|
+
declare function classifyHeadTransition(reflogAction: string | undefined, headParentCount: number | undefined): RepositoryTransition["kind"] | undefined;
|
|
340
|
+
declare function isObservedPush(previous: RepositoryInfo, current: RepositoryInfo, branchChanged?: boolean): boolean;
|
|
316
341
|
declare function detectRepository(cwd?: string): Promise<RepositoryInfo | undefined>;
|
|
342
|
+
declare function observeRepositoryTransitions(source: AgentSource, sessionId: string | undefined, current: RepositoryInfo): Promise<RepositoryTransition[]>;
|
|
317
343
|
declare function detectWorkingTreeMetrics(root: string): Promise<WorkingTreeMetrics | undefined>;
|
|
318
344
|
declare function sanitizeRemote(remote: string): string;
|
|
319
345
|
declare function normalizeGitHubRepository(value: string): string | undefined;
|
|
320
346
|
declare function matchesGitHubRepository(remote: string | undefined, expected: string): boolean;
|
|
321
347
|
|
|
322
|
-
export { type AgentSource, type CanonicalHookEvent, type CredentialStore, type DeviceAuthorization, type DeviceTokenResponse, type FlushResult, type HookEventKind, JsonFileOfflineQueue, MemoryOfflineQueue, type OfflineQueue, PRESENCE_HEARTBEAT_INTERVAL_MS, type PresenceMetrics, type RedactionOptions, type RepositoryInfo, type SafeValue, SignedBatchClient, type SignedBatchClientOptions, type StoredCredential, SystemCredentialStore, type WorkingTreeMetrics, agentSourceSchema, canonicalHookEventSchema, createHookEvent, detectRepository, detectWorkingTreeMetrics, deviceAuthorizationSchema, deviceTokenResponseSchema, eventTypeForHook, hookEventKindSchema, mapClaudeCodeHook, mapCodexHook, mapCursorHook, matchesGitHubRepository, normalizeGitHubRepository, pollDeviceToken, presenceStatePath, redact, requestDeviceAuthorization, runPresenceHeartbeat, safeMetadata, safeValueSchema, sanitizeRemote, startPresenceSession, stopPresenceSession, updatePresenceSession };
|
|
348
|
+
export { type AgentSource, type CanonicalHookEvent, type CredentialStore, type DeviceAuthorization, type DeviceTokenResponse, type FlushResult, type HookEventKind, JsonFileOfflineQueue, MemoryOfflineQueue, type OfflineQueue, PRESENCE_HEARTBEAT_INTERVAL_MS, type PresenceMetrics, type RedactionOptions, type RepositoryInfo, type RepositoryTransition, type SafeValue, SignedBatchClient, type SignedBatchClientOptions, type StoredCredential, SystemCredentialStore, type WorkingTreeMetrics, agentSourceSchema, canonicalHookEventSchema, classifyHeadTransition, createHookEvent, detectRepository, detectWorkingTreeMetrics, deviceAuthorizationSchema, deviceTokenResponseSchema, eventTypeForHook, hookEventKindSchema, isObservedPush, mapClaudeCodeHook, mapCodexHook, mapCursorHook, matchesGitHubRepository, normalizeGitHubRepository, observeRepositoryTransitions, pollDeviceToken, presenceStatePath, redact, requestDeviceAuthorization, runPresenceHeartbeat, safeMetadata, safeValueSchema, sanitizeRemote, startPresenceSession, stopPresenceSession, updatePresenceSession };
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
SystemCredentialStore,
|
|
7
7
|
agentSourceSchema,
|
|
8
8
|
canonicalHookEventSchema,
|
|
9
|
+
classifyHeadTransition,
|
|
9
10
|
createHookEvent,
|
|
10
11
|
detectRepository,
|
|
11
12
|
detectWorkingTreeMetrics,
|
|
@@ -13,11 +14,13 @@ import {
|
|
|
13
14
|
deviceTokenResponseSchema,
|
|
14
15
|
eventTypeForHook,
|
|
15
16
|
hookEventKindSchema,
|
|
17
|
+
isObservedPush,
|
|
16
18
|
mapClaudeCodeHook,
|
|
17
19
|
mapCodexHook,
|
|
18
20
|
mapCursorHook,
|
|
19
21
|
matchesGitHubRepository,
|
|
20
22
|
normalizeGitHubRepository,
|
|
23
|
+
observeRepositoryTransitions,
|
|
21
24
|
pollDeviceToken,
|
|
22
25
|
presenceStatePath,
|
|
23
26
|
redact,
|
|
@@ -29,7 +32,7 @@ import {
|
|
|
29
32
|
startPresenceSession,
|
|
30
33
|
stopPresenceSession,
|
|
31
34
|
updatePresenceSession
|
|
32
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-O4MHMJZZ.js";
|
|
33
36
|
export {
|
|
34
37
|
JsonFileOfflineQueue,
|
|
35
38
|
MemoryOfflineQueue,
|
|
@@ -38,6 +41,7 @@ export {
|
|
|
38
41
|
SystemCredentialStore,
|
|
39
42
|
agentSourceSchema,
|
|
40
43
|
canonicalHookEventSchema,
|
|
44
|
+
classifyHeadTransition,
|
|
41
45
|
createHookEvent,
|
|
42
46
|
detectRepository,
|
|
43
47
|
detectWorkingTreeMetrics,
|
|
@@ -45,11 +49,13 @@ export {
|
|
|
45
49
|
deviceTokenResponseSchema,
|
|
46
50
|
eventTypeForHook,
|
|
47
51
|
hookEventKindSchema,
|
|
52
|
+
isObservedPush,
|
|
48
53
|
mapClaudeCodeHook,
|
|
49
54
|
mapCodexHook,
|
|
50
55
|
mapCursorHook,
|
|
51
56
|
matchesGitHubRepository,
|
|
52
57
|
normalizeGitHubRepository,
|
|
58
|
+
observeRepositoryTransitions,
|
|
53
59
|
pollDeviceToken,
|
|
54
60
|
presenceStatePath,
|
|
55
61
|
redact,
|