@generacy-ai/generacy 0.4.0 → 0.6.0
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/dist/cli/commands/cockpit/clarification-answer-marker.d.ts +8 -0
- package/dist/cli/commands/cockpit/clarification-answer-marker.d.ts.map +1 -0
- package/dist/cli/commands/cockpit/clarification-answer-marker.js +76 -0
- package/dist/cli/commands/cockpit/clarification-answer-marker.js.map +1 -0
- package/dist/cli/commands/cockpit/clarification-comment-finder.d.ts.map +1 -1
- package/dist/cli/commands/cockpit/clarification-comment-finder.js +33 -3
- package/dist/cli/commands/cockpit/clarification-comment-finder.js.map +1 -1
- package/dist/cli/commands/cockpit/clarify-relay.d.ts +38 -0
- package/dist/cli/commands/cockpit/clarify-relay.d.ts.map +1 -0
- package/dist/cli/commands/cockpit/clarify-relay.js +120 -0
- package/dist/cli/commands/cockpit/clarify-relay.js.map +1 -0
- package/dist/cli/commands/cockpit/doorbell/aggregate-on-demand.d.ts +43 -0
- package/dist/cli/commands/cockpit/doorbell/aggregate-on-demand.d.ts.map +1 -0
- package/dist/cli/commands/cockpit/doorbell/aggregate-on-demand.js +72 -0
- package/dist/cli/commands/cockpit/doorbell/aggregate-on-demand.js.map +1 -0
- package/dist/cli/commands/cockpit/doorbell/channel-discovery.d.ts +37 -0
- package/dist/cli/commands/cockpit/doorbell/channel-discovery.d.ts.map +1 -0
- package/dist/cli/commands/cockpit/doorbell/channel-discovery.js +92 -0
- package/dist/cli/commands/cockpit/doorbell/channel-discovery.js.map +1 -0
- package/dist/cli/commands/cockpit/doorbell/smee-source.d.ts +73 -0
- package/dist/cli/commands/cockpit/doorbell/smee-source.d.ts.map +1 -0
- package/dist/cli/commands/cockpit/doorbell/smee-source.js +362 -0
- package/dist/cli/commands/cockpit/doorbell/smee-source.js.map +1 -0
- package/dist/cli/commands/cockpit/doorbell/source-selector.d.ts +56 -0
- package/dist/cli/commands/cockpit/doorbell/source-selector.d.ts.map +1 -0
- package/dist/cli/commands/cockpit/doorbell/source-selector.js +182 -0
- package/dist/cli/commands/cockpit/doorbell/source-selector.js.map +1 -0
- package/dist/cli/commands/cockpit/doorbell/sse-parser.d.ts +15 -0
- package/dist/cli/commands/cockpit/doorbell/sse-parser.d.ts.map +1 -0
- package/dist/cli/commands/cockpit/doorbell/sse-parser.js +43 -0
- package/dist/cli/commands/cockpit/doorbell/sse-parser.js.map +1 -0
- package/dist/cli/commands/cockpit/doorbell/startup-retry.d.ts +55 -0
- package/dist/cli/commands/cockpit/doorbell/startup-retry.d.ts.map +1 -0
- package/dist/cli/commands/cockpit/doorbell/startup-retry.js +159 -0
- package/dist/cli/commands/cockpit/doorbell/startup-retry.js.map +1 -0
- package/dist/cli/commands/cockpit/doorbell/subscribe.d.ts +19 -0
- package/dist/cli/commands/cockpit/doorbell/subscribe.d.ts.map +1 -0
- package/dist/cli/commands/cockpit/doorbell/subscribe.js +54 -0
- package/dist/cli/commands/cockpit/doorbell/subscribe.js.map +1 -0
- package/dist/cli/commands/cockpit/doorbell/webhook-to-event.d.ts +28 -0
- package/dist/cli/commands/cockpit/doorbell/webhook-to-event.d.ts.map +1 -0
- package/dist/cli/commands/cockpit/doorbell/webhook-to-event.js +159 -0
- package/dist/cli/commands/cockpit/doorbell/webhook-to-event.js.map +1 -0
- package/dist/cli/commands/cockpit/doorbell.d.ts +58 -0
- package/dist/cli/commands/cockpit/doorbell.d.ts.map +1 -0
- package/dist/cli/commands/cockpit/doorbell.js +477 -0
- package/dist/cli/commands/cockpit/doorbell.js.map +1 -0
- package/dist/cli/commands/cockpit/index.d.ts +1 -0
- package/dist/cli/commands/cockpit/index.d.ts.map +1 -1
- package/dist/cli/commands/cockpit/index.js +5 -0
- package/dist/cli/commands/cockpit/index.js.map +1 -1
- package/dist/cli/commands/cockpit/mcp/errors.d.ts +1 -1
- package/dist/cli/commands/cockpit/mcp/errors.d.ts.map +1 -1
- package/dist/cli/commands/cockpit/mcp/errors.js.map +1 -1
- package/dist/cli/commands/cockpit/mcp/event-bus-registry.d.ts +2 -1
- package/dist/cli/commands/cockpit/mcp/event-bus-registry.d.ts.map +1 -1
- package/dist/cli/commands/cockpit/mcp/event-bus-registry.js +38 -9
- package/dist/cli/commands/cockpit/mcp/event-bus-registry.js.map +1 -1
- package/dist/cli/commands/cockpit/mcp/schemas.d.ts +184 -1
- package/dist/cli/commands/cockpit/mcp/schemas.d.ts.map +1 -1
- package/dist/cli/commands/cockpit/mcp/schemas.js +43 -2
- package/dist/cli/commands/cockpit/mcp/schemas.js.map +1 -1
- package/dist/cli/commands/cockpit/mcp/server.d.ts.map +1 -1
- package/dist/cli/commands/cockpit/mcp/server.js +16 -1
- package/dist/cli/commands/cockpit/mcp/server.js.map +1 -1
- package/dist/cli/commands/cockpit/mcp/tools/cockpit_queue.d.ts +19 -3
- package/dist/cli/commands/cockpit/mcp/tools/cockpit_queue.d.ts.map +1 -1
- package/dist/cli/commands/cockpit/mcp/tools/cockpit_queue.js +40 -2
- package/dist/cli/commands/cockpit/mcp/tools/cockpit_queue.js.map +1 -1
- package/dist/cli/commands/cockpit/mcp/tools/cockpit_relay_clarify_answers.d.ts +45 -0
- package/dist/cli/commands/cockpit/mcp/tools/cockpit_relay_clarify_answers.d.ts.map +1 -0
- package/dist/cli/commands/cockpit/mcp/tools/cockpit_relay_clarify_answers.js +45 -0
- package/dist/cli/commands/cockpit/mcp/tools/cockpit_relay_clarify_answers.js.map +1 -0
- package/dist/cli/commands/cockpit/mcp/tools/cockpit_scope_add.d.ts +31 -0
- package/dist/cli/commands/cockpit/mcp/tools/cockpit_scope_add.d.ts.map +1 -0
- package/dist/cli/commands/cockpit/mcp/tools/cockpit_scope_add.js +81 -0
- package/dist/cli/commands/cockpit/mcp/tools/cockpit_scope_add.js.map +1 -0
- package/dist/cli/commands/cockpit/mcp/tools/cockpit_scope_remove.d.ts +30 -0
- package/dist/cli/commands/cockpit/mcp/tools/cockpit_scope_remove.d.ts.map +1 -0
- package/dist/cli/commands/cockpit/mcp/tools/cockpit_scope_remove.js +80 -0
- package/dist/cli/commands/cockpit/mcp/tools/cockpit_scope_remove.js.map +1 -0
- package/dist/cli/commands/cockpit/queue.d.ts +15 -0
- package/dist/cli/commands/cockpit/queue.d.ts.map +1 -1
- package/dist/cli/commands/cockpit/queue.js +142 -29
- package/dist/cli/commands/cockpit/queue.js.map +1 -1
- package/dist/cli/commands/cockpit/resume.d.ts +12 -6
- package/dist/cli/commands/cockpit/resume.d.ts.map +1 -1
- package/dist/cli/commands/cockpit/resume.js +27 -11
- package/dist/cli/commands/cockpit/resume.js.map +1 -1
- package/dist/cli/commands/cockpit/scope/errors.d.ts +21 -0
- package/dist/cli/commands/cockpit/scope/errors.d.ts.map +1 -0
- package/dist/cli/commands/cockpit/scope/errors.js +17 -0
- package/dist/cli/commands/cockpit/scope/errors.js.map +1 -0
- package/dist/cli/commands/cockpit/scope/retry.d.ts +24 -0
- package/dist/cli/commands/cockpit/scope/retry.d.ts.map +1 -0
- package/dist/cli/commands/cockpit/scope/retry.js +52 -0
- package/dist/cli/commands/cockpit/scope/retry.js.map +1 -0
- package/dist/cli/commands/cockpit/scope/writer.d.ts +21 -0
- package/dist/cli/commands/cockpit/scope/writer.d.ts.map +1 -0
- package/dist/cli/commands/cockpit/scope/writer.js +141 -0
- package/dist/cli/commands/cockpit/scope/writer.js.map +1 -0
- package/dist/cli/commands/cockpit/scope.d.ts +22 -0
- package/dist/cli/commands/cockpit/scope.d.ts.map +1 -0
- package/dist/cli/commands/cockpit/scope.js +110 -0
- package/dist/cli/commands/cockpit/scope.js.map +1 -0
- package/dist/cli/commands/cockpit/status/group.d.ts +4 -2
- package/dist/cli/commands/cockpit/status/group.d.ts.map +1 -1
- package/dist/cli/commands/cockpit/status/group.js +17 -2
- package/dist/cli/commands/cockpit/status/group.js.map +1 -1
- package/dist/cli/commands/cockpit/status.d.ts.map +1 -1
- package/dist/cli/commands/cockpit/status.js +17 -2
- package/dist/cli/commands/cockpit/status.js.map +1 -1
- package/dist/cli/commands/cockpit/watch/diff.d.ts +6 -2
- package/dist/cli/commands/cockpit/watch/diff.d.ts.map +1 -1
- package/dist/cli/commands/cockpit/watch/diff.js +11 -3
- package/dist/cli/commands/cockpit/watch/diff.js.map +1 -1
- package/dist/cli/commands/cockpit/watch/poll-loop.d.ts +2 -0
- package/dist/cli/commands/cockpit/watch/poll-loop.d.ts.map +1 -1
- package/dist/cli/commands/cockpit/watch/poll-loop.js +38 -6
- package/dist/cli/commands/cockpit/watch/poll-loop.js.map +1 -1
- package/dist/cli/commands/cockpit/watch/pr-state.d.ts +15 -1
- package/dist/cli/commands/cockpit/watch/pr-state.d.ts.map +1 -1
- package/dist/cli/commands/cockpit/watch/pr-state.js +38 -0
- package/dist/cli/commands/cockpit/watch/pr-state.js.map +1 -1
- package/dist/cli/commands/cockpit/watch/snapshot.d.ts +6 -1
- package/dist/cli/commands/cockpit/watch/snapshot.d.ts.map +1 -1
- package/dist/cli/commands/cockpit/watch/snapshot.js +3 -1
- package/dist/cli/commands/cockpit/watch/snapshot.js.map +1 -1
- package/dist/cli/commands/cockpit/watch.d.ts +2 -1
- package/dist/cli/commands/cockpit/watch.d.ts.map +1 -1
- package/dist/cli/commands/cockpit/watch.js +25 -13
- package/dist/cli/commands/cockpit/watch.js.map +1 -1
- package/dist/config/schema.d.ts +788 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +12 -1
- package/dist/config/schema.js.map +1 -1
- package/package.json +6 -6
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `SourceSelector` — owns the runtime demotion/re-promotion state for the
|
|
3
|
+
* doorbell's wake source (Q3=D policy). Emits exactly one FR-006 `source=…`
|
|
4
|
+
* stderr line per transition.
|
|
5
|
+
*
|
|
6
|
+
* Contract: `specs/978-summary-generacy-cockpit/contracts/source-selector.md`.
|
|
7
|
+
*/
|
|
8
|
+
export const DEFAULT_DEMOTE_AFTER_FAILURES = 5;
|
|
9
|
+
export const DEFAULT_DEMOTE_AFTER_MS_WITHOUT_SUCCESS = 300_000;
|
|
10
|
+
export const DEFAULT_RE_PROMOTE_INTERVAL_MS = 300_000;
|
|
11
|
+
const ELAPSED_TICKER_INTERVAL_MS = 1_000;
|
|
12
|
+
function formatLine(next, reason) {
|
|
13
|
+
const label = next === 'poll-fallback' ? 'poll-fallback' : 'smee';
|
|
14
|
+
return `cockpit doorbell: source=${label} reason=${reason}\n`;
|
|
15
|
+
}
|
|
16
|
+
export class SourceSelector {
|
|
17
|
+
_current;
|
|
18
|
+
consecutiveReconnectFailures = 0;
|
|
19
|
+
lastSuccessfulConnectAt = null;
|
|
20
|
+
demotedAt = null;
|
|
21
|
+
rePromoteTimer = null;
|
|
22
|
+
elapsedTicker = null;
|
|
23
|
+
modeChangeCbs = [];
|
|
24
|
+
stopped = false;
|
|
25
|
+
initialWasSmee;
|
|
26
|
+
demoteAfterFailures;
|
|
27
|
+
demoteAfterMsWithoutSuccess;
|
|
28
|
+
rePromoteIntervalMs;
|
|
29
|
+
now;
|
|
30
|
+
stderr;
|
|
31
|
+
logger;
|
|
32
|
+
pendingRePromoteEmit = false;
|
|
33
|
+
constructor(options) {
|
|
34
|
+
this.initialWasSmee = options.initial === 'smee-attempt';
|
|
35
|
+
this.demoteAfterFailures =
|
|
36
|
+
options.demoteAfterConsecutiveFailures ?? DEFAULT_DEMOTE_AFTER_FAILURES;
|
|
37
|
+
this.demoteAfterMsWithoutSuccess =
|
|
38
|
+
options.demoteAfterMsWithoutSuccess ?? DEFAULT_DEMOTE_AFTER_MS_WITHOUT_SUCCESS;
|
|
39
|
+
this.rePromoteIntervalMs =
|
|
40
|
+
options.rePromoteIntervalMs ?? DEFAULT_RE_PROMOTE_INTERVAL_MS;
|
|
41
|
+
this.now = options.now ?? (() => Date.now());
|
|
42
|
+
this.stderr = options.stderr;
|
|
43
|
+
if (options.logger != null)
|
|
44
|
+
this.logger = options.logger;
|
|
45
|
+
this._current = options.initial;
|
|
46
|
+
const startupReason = options.initial === 'smee-attempt' ? 'startup-smee-selected' : 'startup-no-channel';
|
|
47
|
+
this.writeLine(this._current, startupReason);
|
|
48
|
+
if (this.initialWasSmee) {
|
|
49
|
+
this.elapsedTicker = setInterval(() => this.observeElapsed(), ELAPSED_TICKER_INTERVAL_MS);
|
|
50
|
+
if (typeof this.elapsedTicker.unref === 'function')
|
|
51
|
+
this.elapsedTicker.unref();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
get currentSource() {
|
|
55
|
+
return this._current;
|
|
56
|
+
}
|
|
57
|
+
onModeChange(cb) {
|
|
58
|
+
this.modeChangeCbs.push(cb);
|
|
59
|
+
}
|
|
60
|
+
onReconnectAttempt(failedAttempts) {
|
|
61
|
+
if (this.stopped)
|
|
62
|
+
return;
|
|
63
|
+
this.consecutiveReconnectFailures = failedAttempts;
|
|
64
|
+
if (this._current !== 'smee-active' && this._current !== 'smee-attempt')
|
|
65
|
+
return;
|
|
66
|
+
if (failedAttempts >= this.demoteAfterFailures && this._current === 'smee-active') {
|
|
67
|
+
this.transition('poll-fallback', 'smee-runtime-lost');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
onReconnectSuccess() {
|
|
71
|
+
if (this.stopped)
|
|
72
|
+
return;
|
|
73
|
+
this.consecutiveReconnectFailures = 0;
|
|
74
|
+
this.lastSuccessfulConnectAt = this.now();
|
|
75
|
+
if (this._current === 'smee-attempt') {
|
|
76
|
+
const shouldEmit = this.pendingRePromoteEmit;
|
|
77
|
+
this.pendingRePromoteEmit = false;
|
|
78
|
+
if (shouldEmit) {
|
|
79
|
+
this.transition('smee-active', 'smee-re-promoted');
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
this._current = 'smee-active';
|
|
83
|
+
for (const cb of this.modeChangeCbs) {
|
|
84
|
+
try {
|
|
85
|
+
cb('smee-active', 'startup-smee-selected');
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
/* callback errors are swallowed */
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
observeElapsed() {
|
|
95
|
+
if (this.stopped)
|
|
96
|
+
return;
|
|
97
|
+
if (this._current !== 'smee-active')
|
|
98
|
+
return;
|
|
99
|
+
if (this.lastSuccessfulConnectAt == null)
|
|
100
|
+
return;
|
|
101
|
+
const elapsed = this.now() - this.lastSuccessfulConnectAt;
|
|
102
|
+
if (elapsed > this.demoteAfterMsWithoutSuccess) {
|
|
103
|
+
this.transition('poll-fallback', 'smee-runtime-lost');
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
stop() {
|
|
107
|
+
if (this.stopped)
|
|
108
|
+
return;
|
|
109
|
+
this.stopped = true;
|
|
110
|
+
if (this.elapsedTicker != null) {
|
|
111
|
+
clearInterval(this.elapsedTicker);
|
|
112
|
+
this.elapsedTicker = null;
|
|
113
|
+
}
|
|
114
|
+
if (this.rePromoteTimer != null) {
|
|
115
|
+
clearInterval(this.rePromoteTimer);
|
|
116
|
+
this.rePromoteTimer = null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
transition(next, reason) {
|
|
120
|
+
if (this._current === next)
|
|
121
|
+
return;
|
|
122
|
+
const prev = this._current;
|
|
123
|
+
this._current = next;
|
|
124
|
+
if (next === 'poll-fallback') {
|
|
125
|
+
this.demotedAt = this.now();
|
|
126
|
+
if (this.initialWasSmee && this.rePromoteTimer == null) {
|
|
127
|
+
this.rePromoteTimer = setInterval(() => this.tickRePromote(), this.rePromoteIntervalMs);
|
|
128
|
+
if (typeof this.rePromoteTimer.unref === 'function')
|
|
129
|
+
this.rePromoteTimer.unref();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else if (next === 'smee-attempt' && prev === 'poll-fallback') {
|
|
133
|
+
// moving out of poll-fallback via the re-promote timer
|
|
134
|
+
if (this.rePromoteTimer != null) {
|
|
135
|
+
clearInterval(this.rePromoteTimer);
|
|
136
|
+
this.rePromoteTimer = null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
for (const cb of this.modeChangeCbs) {
|
|
140
|
+
try {
|
|
141
|
+
cb(next, reason);
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
/* callback errors are swallowed */
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
this.writeLine(next, reason);
|
|
148
|
+
}
|
|
149
|
+
tickRePromote() {
|
|
150
|
+
if (this.stopped)
|
|
151
|
+
return;
|
|
152
|
+
if (this._current !== 'poll-fallback')
|
|
153
|
+
return;
|
|
154
|
+
// Silent transition to smee-attempt; the eventual line is emitted only if
|
|
155
|
+
// the reconnect succeeds (smee-re-promoted).
|
|
156
|
+
if (this.rePromoteTimer != null) {
|
|
157
|
+
clearInterval(this.rePromoteTimer);
|
|
158
|
+
this.rePromoteTimer = null;
|
|
159
|
+
}
|
|
160
|
+
this._current = 'smee-attempt';
|
|
161
|
+
this.pendingRePromoteEmit = true;
|
|
162
|
+
this.consecutiveReconnectFailures = 0;
|
|
163
|
+
this.demotedAt = null;
|
|
164
|
+
for (const cb of this.modeChangeCbs) {
|
|
165
|
+
try {
|
|
166
|
+
cb('smee-attempt', 'smee-re-promoted');
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
/* callback errors are swallowed */
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
writeLine(next, reason) {
|
|
174
|
+
try {
|
|
175
|
+
this.stderr.write(formatLine(next, reason));
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
/* stderr write failures are swallowed */
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=source-selector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-selector.js","sourceRoot":"","sources":["../../../../../src/cli/commands/cockpit/doorbell/source-selector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `parseSseEventBlock` — parses one SSE frame block from smee.io.
|
|
3
|
+
*
|
|
4
|
+
* Matches the inline parser in
|
|
5
|
+
* `packages/orchestrator/src/services/smee-receiver.ts`. Extracts the
|
|
6
|
+
* `x-github-event` and `body.action` discriminators plus the full body object,
|
|
7
|
+
* dropping `ready`/`ping` heartbeat frames silently. Malformed JSON → null.
|
|
8
|
+
*/
|
|
9
|
+
export interface NormalizedPayload {
|
|
10
|
+
githubEvent: string;
|
|
11
|
+
action: string;
|
|
12
|
+
body: Record<string, unknown>;
|
|
13
|
+
}
|
|
14
|
+
export declare function parseSseEventBlock(text: string): NormalizedPayload | null;
|
|
15
|
+
//# sourceMappingURL=sse-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sse-parser.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/cockpit/doorbell/sse-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI,CAoCzE"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `parseSseEventBlock` — parses one SSE frame block from smee.io.
|
|
3
|
+
*
|
|
4
|
+
* Matches the inline parser in
|
|
5
|
+
* `packages/orchestrator/src/services/smee-receiver.ts`. Extracts the
|
|
6
|
+
* `x-github-event` and `body.action` discriminators plus the full body object,
|
|
7
|
+
* dropping `ready`/`ping` heartbeat frames silently. Malformed JSON → null.
|
|
8
|
+
*/
|
|
9
|
+
export function parseSseEventBlock(text) {
|
|
10
|
+
let eventType = '';
|
|
11
|
+
const dataLines = [];
|
|
12
|
+
for (const line of text.split('\n')) {
|
|
13
|
+
if (line.startsWith('event:')) {
|
|
14
|
+
eventType = line.slice(6).trim();
|
|
15
|
+
}
|
|
16
|
+
else if (line.startsWith('data:')) {
|
|
17
|
+
dataLines.push(line.slice(5).trim());
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
if (eventType !== 'message' && eventType !== '') {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
const dataStr = dataLines.join('\n');
|
|
24
|
+
if (!dataStr)
|
|
25
|
+
return null;
|
|
26
|
+
let data;
|
|
27
|
+
try {
|
|
28
|
+
data = JSON.parse(dataStr);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
const githubEvent = data['x-github-event'];
|
|
34
|
+
if (typeof githubEvent !== 'string' || githubEvent === '')
|
|
35
|
+
return null;
|
|
36
|
+
const rawBody = data['body'];
|
|
37
|
+
if (rawBody == null || typeof rawBody !== 'object')
|
|
38
|
+
return null;
|
|
39
|
+
const body = rawBody;
|
|
40
|
+
const action = typeof body['action'] === 'string' ? body['action'] : '';
|
|
41
|
+
return { githubEvent, action, body };
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=sse-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sse-parser.js","sourceRoot":"","sources":["../../../../../src/cli/commands/cockpit/doorbell/sse-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAQH,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,IAAI,SAAS,GAAG,EAAE,CAAC;IACnB,MAAM,SAAS,GAAa,EAAE,CAAC;IAE/B,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC9B,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACnC,CAAC;aAAM,IAAI,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YACpC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACvC,CAAC;IACH,CAAC;IAED,IAAI,SAAS,KAAK,SAAS,IAAI,SAAS,KAAK,EAAE,EAAE,CAAC;QAChD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAE1B,IAAI,IAA6B,CAAC;IAClC,IAAI,CAAC;QACH,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAA4B,CAAC;IACxD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,WAAW,GAAG,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAC3C,IAAI,OAAO,WAAW,KAAK,QAAQ,IAAI,WAAW,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IAEvE,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;IAC7B,IAAI,OAAO,IAAI,IAAI,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAChE,MAAM,IAAI,GAAG,OAAkC,CAAC;IAEhD,MAAM,MAAM,GAAG,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAE,IAAI,CAAC,QAAQ,CAAY,CAAC,CAAC,CAAC,EAAE,CAAC;IAEpF,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AACvC,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `runStartupRetry` — envelope around a Promise-returning startup task that
|
|
3
|
+
* survives transient failures and exits distinctively on permanent ones.
|
|
4
|
+
* Replaces the doorbell's silent `exit(2)` on first-hiccup failure.
|
|
5
|
+
*
|
|
6
|
+
* Contract: `specs/980-summary-978-shipped-working/contracts/startup-retry.md`.
|
|
7
|
+
*/
|
|
8
|
+
import type { RateLimitScheduler } from '@generacy-ai/cockpit';
|
|
9
|
+
export type GhErrorClass = {
|
|
10
|
+
kind: 'retriable';
|
|
11
|
+
hint: string;
|
|
12
|
+
} | {
|
|
13
|
+
kind: 'permanent';
|
|
14
|
+
reason: string;
|
|
15
|
+
};
|
|
16
|
+
export type StartupRetryLabel = 'acquireEpicBus' | 'resolveEpic';
|
|
17
|
+
export interface StartupRetryOptions<T> {
|
|
18
|
+
task: () => Promise<T>;
|
|
19
|
+
label: StartupRetryLabel;
|
|
20
|
+
rateLimitScheduler: RateLimitScheduler;
|
|
21
|
+
abortSignal: AbortSignal;
|
|
22
|
+
stderr: {
|
|
23
|
+
write(chunk: string): boolean | void;
|
|
24
|
+
};
|
|
25
|
+
logger: {
|
|
26
|
+
warn: (msg: string) => void;
|
|
27
|
+
info?: (msg: string) => void;
|
|
28
|
+
};
|
|
29
|
+
now?: () => number;
|
|
30
|
+
sleep?: (ms: number, signal: AbortSignal) => Promise<void>;
|
|
31
|
+
classify?: (err: unknown) => GhErrorClass;
|
|
32
|
+
initialWindowMs?: number;
|
|
33
|
+
lateWindowIntervalMs?: number;
|
|
34
|
+
}
|
|
35
|
+
export type StartupRetryOutcome<T> = {
|
|
36
|
+
kind: 'success';
|
|
37
|
+
value: T;
|
|
38
|
+
} | {
|
|
39
|
+
kind: 'permanent';
|
|
40
|
+
reason: string;
|
|
41
|
+
} | {
|
|
42
|
+
kind: 'aborted';
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Deterministic mapping from a raw `gh`-call error to `GhErrorClass`.
|
|
46
|
+
*
|
|
47
|
+
* Retriable rules are evaluated first (node error codes → socket-hang-up →
|
|
48
|
+
* HTTP 429/5xx). Permanent rules follow (401/Bad credentials → 403 SAML/
|
|
49
|
+
* scope → 404 not-found → JSON-parse failure). Default: permanent/unknown.
|
|
50
|
+
*
|
|
51
|
+
* See `contracts/startup-retry.md § Error classifier` for full rationale.
|
|
52
|
+
*/
|
|
53
|
+
export declare function classifyGhError(err: unknown): GhErrorClass;
|
|
54
|
+
export declare function runStartupRetry<T>(opts: StartupRetryOptions<T>): Promise<StartupRetryOutcome<T>>;
|
|
55
|
+
//# sourceMappingURL=startup-retry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"startup-retry.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/cockpit/doorbell/startup-retry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE/D,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1C,MAAM,MAAM,iBAAiB,GAAG,gBAAgB,GAAG,aAAa,CAAC;AAEjE,MAAM,WAAW,mBAAmB,CAAC,CAAC;IACpC,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,CAAC;IACvB,KAAK,EAAE,iBAAiB,CAAC;IACzB,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,WAAW,EAAE,WAAW,CAAC;IACzB,MAAM,EAAE;QAAE,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAAA;KAAE,CAAC;IACjD,MAAM,EAAE;QAAE,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;IACtE,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,YAAY,CAAC;IAC1C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,MAAM,mBAAmB,CAAC,CAAC,IAC7B;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GAC7B;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACrC;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC;AAyBxB;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,YAAY,CAyD1D;AAoBD,wBAAsB,eAAe,CAAC,CAAC,EACrC,IAAI,EAAE,mBAAmB,CAAC,CAAC,CAAC,GAC3B,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAqFjC"}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
const DEFAULT_INITIAL_WINDOW_MS = 2 * 60_000;
|
|
2
|
+
const DEFAULT_LATE_WINDOW_INTERVAL_MS = 5 * 60_000;
|
|
3
|
+
function errMessage(err) {
|
|
4
|
+
if (err == null)
|
|
5
|
+
return '';
|
|
6
|
+
if (err instanceof Error)
|
|
7
|
+
return err.message;
|
|
8
|
+
return String(err);
|
|
9
|
+
}
|
|
10
|
+
function errCode(err) {
|
|
11
|
+
if (err == null || typeof err !== 'object')
|
|
12
|
+
return undefined;
|
|
13
|
+
const code = err.code;
|
|
14
|
+
return typeof code === 'string' ? code : undefined;
|
|
15
|
+
}
|
|
16
|
+
const RETRIABLE_NODE_CODES = new Set([
|
|
17
|
+
'ECONNRESET',
|
|
18
|
+
'ETIMEDOUT',
|
|
19
|
+
'ENOTFOUND',
|
|
20
|
+
'ECONNREFUSED',
|
|
21
|
+
'EPIPE',
|
|
22
|
+
]);
|
|
23
|
+
/**
|
|
24
|
+
* Deterministic mapping from a raw `gh`-call error to `GhErrorClass`.
|
|
25
|
+
*
|
|
26
|
+
* Retriable rules are evaluated first (node error codes → socket-hang-up →
|
|
27
|
+
* HTTP 429/5xx). Permanent rules follow (401/Bad credentials → 403 SAML/
|
|
28
|
+
* scope → 404 not-found → JSON-parse failure). Default: permanent/unknown.
|
|
29
|
+
*
|
|
30
|
+
* See `contracts/startup-retry.md § Error classifier` for full rationale.
|
|
31
|
+
*/
|
|
32
|
+
export function classifyGhError(err) {
|
|
33
|
+
const message = errMessage(err);
|
|
34
|
+
const code = errCode(err);
|
|
35
|
+
// Retriable: node error codes.
|
|
36
|
+
if (code != null && RETRIABLE_NODE_CODES.has(code)) {
|
|
37
|
+
return { kind: 'retriable', hint: code.toLowerCase() };
|
|
38
|
+
}
|
|
39
|
+
// Retriable: socket hang up.
|
|
40
|
+
if (/socket hang up/i.test(message)) {
|
|
41
|
+
return { kind: 'retriable', hint: 'socket-hang-up' };
|
|
42
|
+
}
|
|
43
|
+
// Retriable: HTTP 429 / 5xx.
|
|
44
|
+
const retriableHttp = message.match(/\bHTTP\s+(429|500|502|503|504)\b/);
|
|
45
|
+
if (retriableHttp) {
|
|
46
|
+
return { kind: 'retriable', hint: `http-${retriableHttp[1]}` };
|
|
47
|
+
}
|
|
48
|
+
// Retriable: GitHub primary/secondary rate limits. These do NOT reliably
|
|
49
|
+
// surface as "HTTP 429" — the GraphQL primary limit arrives as plain text
|
|
50
|
+
// ("API rate limit exceeded" / "...rate limit already exceeded...") with no
|
|
51
|
+
// HTTP status, and the secondary/abuse limit arrives as HTTP 403. They MUST
|
|
52
|
+
// be matched here, before the permanent 401/403 rules below, or a
|
|
53
|
+
// rate-limited `gh` call is misclassified as permanent and the doorbell
|
|
54
|
+
// exit(3)s instead of retrying — the exact failure this envelope exists to
|
|
55
|
+
// prevent, since rate-limiting is the dominant transient `gh` failure.
|
|
56
|
+
if (/rate limit (?:already )?exceeded/i.test(message) ||
|
|
57
|
+
/secondary rate limit/i.test(message) ||
|
|
58
|
+
/abuse detection/i.test(message)) {
|
|
59
|
+
return { kind: 'retriable', hint: 'rate-limit' };
|
|
60
|
+
}
|
|
61
|
+
// Permanent: 401 / Bad credentials.
|
|
62
|
+
if (/\bHTTP\s+401\b/.test(message) || /Bad credentials/i.test(message)) {
|
|
63
|
+
return { kind: 'permanent', reason: 'bad-credentials' };
|
|
64
|
+
}
|
|
65
|
+
// Permanent: 403 / SAML / scope.
|
|
66
|
+
if (/\bHTTP\s+403\b/.test(message) ||
|
|
67
|
+
/SAML|scope|not accessible by/i.test(message)) {
|
|
68
|
+
return { kind: 'permanent', reason: 'scope-or-sso' };
|
|
69
|
+
}
|
|
70
|
+
// Permanent: 404 / not-found.
|
|
71
|
+
if (/\bHTTP\s+404\b/.test(message) ||
|
|
72
|
+
/Could not resolve to (an Issue|a Repository)/i.test(message)) {
|
|
73
|
+
return { kind: 'permanent', reason: 'not-found' };
|
|
74
|
+
}
|
|
75
|
+
// Permanent: malformed output.
|
|
76
|
+
if (/parsing|expected JSON|invalid character/i.test(message)) {
|
|
77
|
+
return { kind: 'permanent', reason: 'malformed-output' };
|
|
78
|
+
}
|
|
79
|
+
return { kind: 'permanent', reason: 'unknown' };
|
|
80
|
+
}
|
|
81
|
+
function defaultSleep(ms, signal) {
|
|
82
|
+
return new Promise((resolve) => {
|
|
83
|
+
if (signal.aborted) {
|
|
84
|
+
resolve();
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const timer = setTimeout(() => {
|
|
88
|
+
signal.removeEventListener('abort', onAbort);
|
|
89
|
+
resolve();
|
|
90
|
+
}, ms);
|
|
91
|
+
const onAbort = () => {
|
|
92
|
+
clearTimeout(timer);
|
|
93
|
+
resolve();
|
|
94
|
+
};
|
|
95
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
export async function runStartupRetry(opts) {
|
|
99
|
+
const { task, label, rateLimitScheduler, abortSignal, stderr, logger, now = () => Date.now(), sleep = defaultSleep, classify = classifyGhError, initialWindowMs = DEFAULT_INITIAL_WINDOW_MS, lateWindowIntervalMs = DEFAULT_LATE_WINDOW_INTERVAL_MS, } = opts;
|
|
100
|
+
const startedAt = now();
|
|
101
|
+
let attempt = 0;
|
|
102
|
+
let firstRetriableEmitted = false;
|
|
103
|
+
let phase = 'initial';
|
|
104
|
+
let recoveredFromLate = false;
|
|
105
|
+
for (;;) {
|
|
106
|
+
if (abortSignal.aborted)
|
|
107
|
+
return { kind: 'aborted' };
|
|
108
|
+
attempt += 1;
|
|
109
|
+
try {
|
|
110
|
+
const value = await task();
|
|
111
|
+
if (recoveredFromLate) {
|
|
112
|
+
stderr.write(`cockpit doorbell: startup-retry-recovered label=${label}\n`);
|
|
113
|
+
}
|
|
114
|
+
return { kind: 'success', value };
|
|
115
|
+
}
|
|
116
|
+
catch (err) {
|
|
117
|
+
const cls = classify(err);
|
|
118
|
+
if (cls.kind === 'permanent') {
|
|
119
|
+
stderr.write(`cockpit doorbell: permanent-error label=${label} reason=${cls.reason}\n`);
|
|
120
|
+
return { kind: 'permanent', reason: cls.reason };
|
|
121
|
+
}
|
|
122
|
+
// Retriable branch.
|
|
123
|
+
if (phase === 'initial') {
|
|
124
|
+
if (!firstRetriableEmitted) {
|
|
125
|
+
stderr.write(`cockpit doorbell: startup-retry label=${label} reason=${cls.hint} attempt=${attempt}\n`);
|
|
126
|
+
firstRetriableEmitted = true;
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
logger.info?.(`cockpit doorbell: startup-retry label=${label} reason=${cls.hint} attempt=${attempt}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
logger.info?.(`cockpit doorbell: startup-retry-late label=${label} reason=${cls.hint} attempt=${attempt}`);
|
|
134
|
+
}
|
|
135
|
+
if (phase === 'initial' && now() - startedAt >= initialWindowMs) {
|
|
136
|
+
stderr.write(`cockpit doorbell: startup-retry-exhausted label=${label} transitioning to late-startup retry\n`);
|
|
137
|
+
phase = 'late';
|
|
138
|
+
recoveredFromLate = true;
|
|
139
|
+
}
|
|
140
|
+
const sleepMs = phase === 'initial'
|
|
141
|
+
? rateLimitScheduler.getCurrentIntervalMs()
|
|
142
|
+
: lateWindowIntervalMs;
|
|
143
|
+
await sleep(sleepMs, abortSignal);
|
|
144
|
+
if (abortSignal.aborted)
|
|
145
|
+
return { kind: 'aborted' };
|
|
146
|
+
// Best-effort hook so the scheduler's watermark logic advances between
|
|
147
|
+
// attempts. Failures are swallowed — the scheduler is defensive.
|
|
148
|
+
if (phase === 'initial') {
|
|
149
|
+
try {
|
|
150
|
+
rateLimitScheduler.noteResponseHeaders({});
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
/* best-effort */
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=startup-retry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"startup-retry.js","sourceRoot":"","sources":["../../../../../src/cli/commands/cockpit/doorbell/startup-retry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `subscribeAndEmit` — drives a `bus.waitFor` loop and writes one stdout line
|
|
3
|
+
* per emitted event to the caller-supplied stdout target. Pure translator
|
|
4
|
+
* lives in `lineForEvent`.
|
|
5
|
+
*
|
|
6
|
+
* Contract: `contracts/subscribe-and-emit.md`.
|
|
7
|
+
*/
|
|
8
|
+
import type { EpicEventBus } from '../mcp/event-bus.js';
|
|
9
|
+
import type { CockpitStreamEvent } from '../watch/stream-event.js';
|
|
10
|
+
export interface SubscribeEmitOptions {
|
|
11
|
+
stdout: {
|
|
12
|
+
write(chunk: string, cb?: () => void): boolean | void;
|
|
13
|
+
};
|
|
14
|
+
onEmit?: (event: CockpitStreamEvent) => void;
|
|
15
|
+
}
|
|
16
|
+
export type SubscribeUnsubscribe = () => void;
|
|
17
|
+
export declare function lineForEvent(event: CockpitStreamEvent): string;
|
|
18
|
+
export declare function subscribeAndEmit(bus: EpicEventBus, options: SubscribeEmitOptions): SubscribeUnsubscribe;
|
|
19
|
+
//# sourceMappingURL=subscribe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscribe.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/cockpit/doorbell/subscribe.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAEnE,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE;QAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,GAAG,IAAI,CAAA;KAAE,CAAC;IAClE,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,IAAI,CAAC;CAC9C;AAED,MAAM,MAAM,oBAAoB,GAAG,MAAM,IAAI,CAAC;AAM9C,wBAAgB,YAAY,CAAC,KAAK,EAAE,kBAAkB,GAAG,MAAM,CAE9D;AAED,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,YAAY,EACjB,OAAO,EAAE,oBAAoB,GAC5B,oBAAoB,CA6CtB"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const WAIT_MS = 60_000;
|
|
2
|
+
const COALESCE_WINDOW_MS = 0;
|
|
3
|
+
const MAX_BATCH_SIZE = 100;
|
|
4
|
+
export function lineForEvent(event) {
|
|
5
|
+
return `${event.type}\n`;
|
|
6
|
+
}
|
|
7
|
+
export function subscribeAndEmit(bus, options) {
|
|
8
|
+
let stopped = false;
|
|
9
|
+
let resolveStop = null;
|
|
10
|
+
const stopSignal = new Promise((resolve) => {
|
|
11
|
+
resolveStop = resolve;
|
|
12
|
+
});
|
|
13
|
+
const loop = async () => {
|
|
14
|
+
let sinceCursor = 0;
|
|
15
|
+
while (!stopped) {
|
|
16
|
+
const waitPromise = bus.waitFor({
|
|
17
|
+
sinceCursor,
|
|
18
|
+
maxWaitMs: WAIT_MS,
|
|
19
|
+
coalesceWindowMs: COALESCE_WINDOW_MS,
|
|
20
|
+
maxBatchSize: MAX_BATCH_SIZE,
|
|
21
|
+
});
|
|
22
|
+
const result = await Promise.race([
|
|
23
|
+
waitPromise.then((r) => ({ kind: 'entries', r })),
|
|
24
|
+
stopSignal.then(() => ({ kind: 'stopped' })),
|
|
25
|
+
]);
|
|
26
|
+
if (result.kind === 'stopped')
|
|
27
|
+
return;
|
|
28
|
+
if (stopped)
|
|
29
|
+
return;
|
|
30
|
+
for (const entry of result.r.entries) {
|
|
31
|
+
if (stopped)
|
|
32
|
+
return;
|
|
33
|
+
const line = lineForEvent(entry.event);
|
|
34
|
+
await new Promise((resolve) => {
|
|
35
|
+
options.stdout.write(line, () => resolve());
|
|
36
|
+
});
|
|
37
|
+
sinceCursor = entry.cursor;
|
|
38
|
+
options.onEmit?.(entry.event);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
void loop();
|
|
43
|
+
return () => {
|
|
44
|
+
if (stopped)
|
|
45
|
+
return;
|
|
46
|
+
stopped = true;
|
|
47
|
+
if (resolveStop != null) {
|
|
48
|
+
const r = resolveStop;
|
|
49
|
+
resolveStop = null;
|
|
50
|
+
r();
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=subscribe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscribe.js","sourceRoot":"","sources":["../../../../../src/cli/commands/cockpit/doorbell/subscribe.ts"],"names":[],"mappings":"AAiBA,MAAM,OAAO,GAAG,MAAM,CAAC;AACvB,MAAM,kBAAkB,GAAG,CAAC,CAAC;AAC7B,MAAM,cAAc,GAAG,GAAG,CAAC;AAE3B,MAAM,UAAU,YAAY,CAAC,KAAyB;IACpD,OAAO,GAAG,KAAK,CAAC,IAAI,IAAI,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,GAAiB,EACjB,OAA6B;IAE7B,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,WAAW,GAAwB,IAAI,CAAC;IAC5C,MAAM,UAAU,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAC/C,WAAW,GAAG,OAAO,CAAC;IACxB,CAAC,CAAC,CAAC;IAEH,MAAM,IAAI,GAAG,KAAK,IAAmB,EAAE;QACrC,IAAI,WAAW,GAAG,CAAC,CAAC;QACpB,OAAO,CAAC,OAAO,EAAE,CAAC;YAChB,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC;gBAC9B,WAAW;gBACX,SAAS,EAAE,OAAO;gBAClB,gBAAgB,EAAE,kBAAkB;gBACpC,YAAY,EAAE,cAAc;aAC7B,CAAC,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;gBAChC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,SAAkB,EAAE,CAAC,EAAE,CAAC,CAAC;gBAC1D,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,SAAkB,EAAE,CAAC,CAAC;aACtD,CAAC,CAAC;YACH,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS;gBAAE,OAAO;YACtC,IAAI,OAAO;gBAAE,OAAO;YACpB,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;gBACrC,IAAI,OAAO;oBAAE,OAAO;gBACpB,MAAM,IAAI,GAAG,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;gBACvC,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;oBAClC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;gBAC9C,CAAC,CAAC,CAAC;gBACH,WAAW,GAAG,KAAK,CAAC,MAAM,CAAC;gBAC3B,OAAO,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAChC,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IAEF,KAAK,IAAI,EAAE,CAAC;IAEZ,OAAO,GAAG,EAAE;QACV,IAAI,OAAO;YAAE,OAAO;QACpB,OAAO,GAAG,IAAI,CAAC;QACf,IAAI,WAAW,IAAI,IAAI,EAAE,CAAC;YACxB,MAAM,CAAC,GAAG,WAAW,CAAC;YACtB,WAAW,GAAG,IAAI,CAAC;YACnB,CAAC,EAAE,CAAC;QACN,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `webhookToStreamEvent` — pure Q1=A mapping. Takes a normalized
|
|
3
|
+
* `{ githubEvent, action, body }` triple, the epic's ref-set filter, and
|
|
4
|
+
* returns one `CockpitStreamEvent` per matched ref (or null when the payload
|
|
5
|
+
* is out of scope).
|
|
6
|
+
*
|
|
7
|
+
* Contract: `specs/978-summary-generacy-cockpit/contracts/webhook-to-event-mapping.md`.
|
|
8
|
+
*
|
|
9
|
+
* Reuses `CockpitEventSchema` from `../watch/emit.ts` — does NOT extend the
|
|
10
|
+
* enum. `from`/`to` are best-effort null (see contract § "Emitted shape").
|
|
11
|
+
*/
|
|
12
|
+
import type { CockpitEventValidated } from '../watch/emit.js';
|
|
13
|
+
export interface RefSetView {
|
|
14
|
+
epicRef: string;
|
|
15
|
+
epicNumber: number;
|
|
16
|
+
epicRepo: string;
|
|
17
|
+
issues: Set<string>;
|
|
18
|
+
prs: Set<string>;
|
|
19
|
+
watchedRepos: Set<string>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Q1=A mapping. Returns exactly one `CockpitEventValidated` for `issues.*` and
|
|
23
|
+
* `pull_request.*` matches, or one per matched PR for `check_run.completed` /
|
|
24
|
+
* `check_suite.completed`. Callers that want single-event semantics should
|
|
25
|
+
* take the first element.
|
|
26
|
+
*/
|
|
27
|
+
export declare function webhookToStreamEvent(githubEvent: string, action: string, body: Record<string, unknown>, refSet: RefSetView, now: () => string): CockpitEventValidated | CockpitEventValidated[] | null;
|
|
28
|
+
//# sourceMappingURL=webhook-to-event.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook-to-event.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/cockpit/doorbell/webhook-to-event.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAE9D,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACpB,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACjB,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC3B;AAiHD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,MAAM,EAAE,UAAU,EAClB,GAAG,EAAE,MAAM,MAAM,GAChB,qBAAqB,GAAG,qBAAqB,EAAE,GAAG,IAAI,CAyExD"}
|