@generacy-ai/generacy 0.5.0 → 0.7.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.
Files changed (105) hide show
  1. package/dist/cli/commands/cockpit/clarification-answer-marker.d.ts +8 -0
  2. package/dist/cli/commands/cockpit/clarification-answer-marker.d.ts.map +1 -0
  3. package/dist/cli/commands/cockpit/clarification-answer-marker.js +76 -0
  4. package/dist/cli/commands/cockpit/clarification-answer-marker.js.map +1 -0
  5. package/dist/cli/commands/cockpit/clarification-comment-finder.d.ts +0 -10
  6. package/dist/cli/commands/cockpit/clarification-comment-finder.d.ts.map +1 -1
  7. package/dist/cli/commands/cockpit/clarification-comment-finder.js +57 -4
  8. package/dist/cli/commands/cockpit/clarification-comment-finder.js.map +1 -1
  9. package/dist/cli/commands/cockpit/clarify-relay.d.ts +38 -0
  10. package/dist/cli/commands/cockpit/clarify-relay.d.ts.map +1 -0
  11. package/dist/cli/commands/cockpit/clarify-relay.js +120 -0
  12. package/dist/cli/commands/cockpit/clarify-relay.js.map +1 -0
  13. package/dist/cli/commands/cockpit/doorbell/aggregate-on-demand.d.ts +43 -0
  14. package/dist/cli/commands/cockpit/doorbell/aggregate-on-demand.d.ts.map +1 -0
  15. package/dist/cli/commands/cockpit/doorbell/aggregate-on-demand.js +72 -0
  16. package/dist/cli/commands/cockpit/doorbell/aggregate-on-demand.js.map +1 -0
  17. package/dist/cli/commands/cockpit/doorbell/channel-discovery.d.ts +103 -0
  18. package/dist/cli/commands/cockpit/doorbell/channel-discovery.d.ts.map +1 -0
  19. package/dist/cli/commands/cockpit/doorbell/channel-discovery.js +168 -0
  20. package/dist/cli/commands/cockpit/doorbell/channel-discovery.js.map +1 -0
  21. package/dist/cli/commands/cockpit/doorbell/smee-source.d.ts +73 -0
  22. package/dist/cli/commands/cockpit/doorbell/smee-source.d.ts.map +1 -0
  23. package/dist/cli/commands/cockpit/doorbell/smee-source.js +405 -0
  24. package/dist/cli/commands/cockpit/doorbell/smee-source.js.map +1 -0
  25. package/dist/cli/commands/cockpit/doorbell/source-selector.d.ts +61 -0
  26. package/dist/cli/commands/cockpit/doorbell/source-selector.d.ts.map +1 -0
  27. package/dist/cli/commands/cockpit/doorbell/source-selector.js +208 -0
  28. package/dist/cli/commands/cockpit/doorbell/source-selector.js.map +1 -0
  29. package/dist/cli/commands/cockpit/doorbell/sse-parser.d.ts +15 -0
  30. package/dist/cli/commands/cockpit/doorbell/sse-parser.d.ts.map +1 -0
  31. package/dist/cli/commands/cockpit/doorbell/sse-parser.js +43 -0
  32. package/dist/cli/commands/cockpit/doorbell/sse-parser.js.map +1 -0
  33. package/dist/cli/commands/cockpit/doorbell/startup-retry.d.ts +55 -0
  34. package/dist/cli/commands/cockpit/doorbell/startup-retry.d.ts.map +1 -0
  35. package/dist/cli/commands/cockpit/doorbell/startup-retry.js +159 -0
  36. package/dist/cli/commands/cockpit/doorbell/startup-retry.js.map +1 -0
  37. package/dist/cli/commands/cockpit/doorbell/subscribe.d.ts +19 -0
  38. package/dist/cli/commands/cockpit/doorbell/subscribe.d.ts.map +1 -0
  39. package/dist/cli/commands/cockpit/doorbell/subscribe.js +54 -0
  40. package/dist/cli/commands/cockpit/doorbell/subscribe.js.map +1 -0
  41. package/dist/cli/commands/cockpit/doorbell/webhook-target-resolver.d.ts +20 -0
  42. package/dist/cli/commands/cockpit/doorbell/webhook-target-resolver.d.ts.map +1 -0
  43. package/dist/cli/commands/cockpit/doorbell/webhook-target-resolver.js +51 -0
  44. package/dist/cli/commands/cockpit/doorbell/webhook-target-resolver.js.map +1 -0
  45. package/dist/cli/commands/cockpit/doorbell/webhook-to-event.d.ts +28 -0
  46. package/dist/cli/commands/cockpit/doorbell/webhook-to-event.d.ts.map +1 -0
  47. package/dist/cli/commands/cockpit/doorbell/webhook-to-event.js +162 -0
  48. package/dist/cli/commands/cockpit/doorbell/webhook-to-event.js.map +1 -0
  49. package/dist/cli/commands/cockpit/doorbell.d.ts +58 -0
  50. package/dist/cli/commands/cockpit/doorbell.d.ts.map +1 -0
  51. package/dist/cli/commands/cockpit/doorbell.js +519 -0
  52. package/dist/cli/commands/cockpit/doorbell.js.map +1 -0
  53. package/dist/cli/commands/cockpit/index.d.ts +1 -0
  54. package/dist/cli/commands/cockpit/index.d.ts.map +1 -1
  55. package/dist/cli/commands/cockpit/index.js +3 -0
  56. package/dist/cli/commands/cockpit/index.js.map +1 -1
  57. package/dist/cli/commands/cockpit/mcp/event-bus-registry.d.ts +3 -2
  58. package/dist/cli/commands/cockpit/mcp/event-bus-registry.d.ts.map +1 -1
  59. package/dist/cli/commands/cockpit/mcp/event-bus-registry.js +41 -12
  60. package/dist/cli/commands/cockpit/mcp/event-bus-registry.js.map +1 -1
  61. package/dist/cli/commands/cockpit/mcp/event-bus.d.ts +6 -0
  62. package/dist/cli/commands/cockpit/mcp/event-bus.d.ts.map +1 -1
  63. package/dist/cli/commands/cockpit/mcp/event-bus.js +7 -1
  64. package/dist/cli/commands/cockpit/mcp/event-bus.js.map +1 -1
  65. package/dist/cli/commands/cockpit/mcp/schemas.d.ts +48 -0
  66. package/dist/cli/commands/cockpit/mcp/schemas.d.ts.map +1 -1
  67. package/dist/cli/commands/cockpit/mcp/schemas.js +28 -0
  68. package/dist/cli/commands/cockpit/mcp/schemas.js.map +1 -1
  69. package/dist/cli/commands/cockpit/mcp/server.d.ts.map +1 -1
  70. package/dist/cli/commands/cockpit/mcp/server.js +6 -1
  71. package/dist/cli/commands/cockpit/mcp/server.js.map +1 -1
  72. package/dist/cli/commands/cockpit/mcp/tools/cockpit_relay_clarify_answers.d.ts +45 -0
  73. package/dist/cli/commands/cockpit/mcp/tools/cockpit_relay_clarify_answers.d.ts.map +1 -0
  74. package/dist/cli/commands/cockpit/mcp/tools/cockpit_relay_clarify_answers.js +45 -0
  75. package/dist/cli/commands/cockpit/mcp/tools/cockpit_relay_clarify_answers.js.map +1 -0
  76. package/dist/cli/commands/cockpit/resume.d.ts +12 -6
  77. package/dist/cli/commands/cockpit/resume.d.ts.map +1 -1
  78. package/dist/cli/commands/cockpit/resume.js +27 -11
  79. package/dist/cli/commands/cockpit/resume.js.map +1 -1
  80. package/dist/cli/commands/cockpit/watch/diff.d.ts +1 -0
  81. package/dist/cli/commands/cockpit/watch/diff.d.ts.map +1 -1
  82. package/dist/cli/commands/cockpit/watch/diff.js.map +1 -1
  83. package/dist/cli/commands/cockpit/watch/emit.d.ts +3 -0
  84. package/dist/cli/commands/cockpit/watch/emit.d.ts.map +1 -1
  85. package/dist/cli/commands/cockpit/watch/emit.js +1 -0
  86. package/dist/cli/commands/cockpit/watch/emit.js.map +1 -1
  87. package/dist/cli/commands/cockpit/watch/poll-loop.d.ts +2 -0
  88. package/dist/cli/commands/cockpit/watch/poll-loop.d.ts.map +1 -1
  89. package/dist/cli/commands/cockpit/watch/poll-loop.js +38 -6
  90. package/dist/cli/commands/cockpit/watch/poll-loop.js.map +1 -1
  91. package/dist/cli/commands/cockpit/watch/pr-state.d.ts +15 -1
  92. package/dist/cli/commands/cockpit/watch/pr-state.d.ts.map +1 -1
  93. package/dist/cli/commands/cockpit/watch/pr-state.js +38 -0
  94. package/dist/cli/commands/cockpit/watch/pr-state.js.map +1 -1
  95. package/dist/cli/commands/cockpit/watch/snapshot.d.ts +6 -1
  96. package/dist/cli/commands/cockpit/watch/snapshot.d.ts.map +1 -1
  97. package/dist/cli/commands/cockpit/watch/snapshot.js +3 -1
  98. package/dist/cli/commands/cockpit/watch/snapshot.js.map +1 -1
  99. package/dist/cli/commands/cockpit/watch/stream-event.d.ts +3 -0
  100. package/dist/cli/commands/cockpit/watch/stream-event.d.ts.map +1 -1
  101. package/dist/cli/commands/cockpit/watch.d.ts +2 -1
  102. package/dist/cli/commands/cockpit/watch.d.ts.map +1 -1
  103. package/dist/cli/commands/cockpit/watch.js +25 -13
  104. package/dist/cli/commands/cockpit/watch.js.map +1 -1
  105. package/package.json +6 -5
@@ -0,0 +1,8 @@
1
+ export interface ClarificationAnswerMarker {
2
+ batch: number;
3
+ answers: Record<number, string>;
4
+ actor?: string;
5
+ ts: string;
6
+ }
7
+ export declare function formatClarificationAnswerComment(marker: ClarificationAnswerMarker): string;
8
+ //# sourceMappingURL=clarification-answer-marker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clarification-answer-marker.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/cockpit/clarification-answer-marker.ts"],"names":[],"mappings":"AAiBA,MAAM,WAAW,yBAAyB;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,wBAAgB,gCAAgC,CAC9C,MAAM,EAAE,yBAAyB,GAChC,MAAM,CAmBR"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * #958 — `formatClarificationAnswerComment` renders the marker-stamped answer
3
+ * comment posted by `cockpit_relay_clarify_answers`. Mirrors the deterministic
4
+ * validation pattern in `manual-advance-marker.ts`.
5
+ *
6
+ * The header line matches `commentCarriesAnswerMarker` in
7
+ * `packages/orchestrator/src/worker/clarification-markers.ts` — this is the
8
+ * sole writer of the `<!-- generacy-clarification-answers:<batch> -->`
9
+ * prefix. Agents never free-write it.
10
+ *
11
+ * Inputs are regex-validated before interpolation so a malformed `batch`,
12
+ * `actor`, or `ts` cannot inject markdown/HTML into the marker body.
13
+ */
14
+ import { PENDING_ANSWER_LITERAL } from '@generacy-ai/workflow-engine';
15
+ const ACTOR_REGEX = /^[A-Za-z0-9-]+$/;
16
+ export function formatClarificationAnswerComment(marker) {
17
+ validate(marker);
18
+ const { batch, answers, actor, ts } = marker;
19
+ const hasActor = typeof actor === 'string' && actor.length > 0;
20
+ const header = hasActor
21
+ ? `<!-- generacy-clarification-answers:${batch} actor=${actor} ts=${ts} -->`
22
+ : `<!-- generacy-clarification-answers:${batch} ts=${ts} -->`;
23
+ const orderedKeys = Object.keys(answers)
24
+ .map((k) => Number(k))
25
+ .sort((a, b) => a - b);
26
+ const answerLines = orderedKeys.map((n) => `Q${n}: ${answers[n]}`);
27
+ return [
28
+ header,
29
+ '',
30
+ `## Answers — batch ${batch}`,
31
+ '',
32
+ ...answerLines,
33
+ '',
34
+ ].join('\n');
35
+ }
36
+ function validate(marker) {
37
+ if (!Number.isInteger(marker.batch) || marker.batch < 0) {
38
+ throw new Error(`formatClarificationAnswerComment: batch must be a non-negative integer (got ${String(marker.batch)})`);
39
+ }
40
+ if (typeof marker.actor === 'string' && marker.actor.length > 0) {
41
+ if (!ACTOR_REGEX.test(marker.actor)) {
42
+ throw new Error(`formatClarificationAnswerComment: invalid actor login "${marker.actor}"`);
43
+ }
44
+ }
45
+ if (typeof marker.ts !== 'string' || marker.ts === '') {
46
+ throw new Error('formatClarificationAnswerComment: ts must be a non-empty ISO-8601 string');
47
+ }
48
+ const parsed = new Date(marker.ts);
49
+ if (Number.isNaN(parsed.getTime()) || parsed.toISOString() !== marker.ts) {
50
+ throw new Error(`formatClarificationAnswerComment: ts "${marker.ts}" is not round-trip ISO-8601`);
51
+ }
52
+ if (marker.answers == null || typeof marker.answers !== 'object') {
53
+ throw new Error('formatClarificationAnswerComment: answers must be a Record<number, string>');
54
+ }
55
+ const keys = Object.keys(marker.answers);
56
+ if (keys.length === 0) {
57
+ throw new Error('formatClarificationAnswerComment: answers map is empty');
58
+ }
59
+ for (const key of keys) {
60
+ const n = Number(key);
61
+ if (!Number.isInteger(n) || n <= 0) {
62
+ throw new Error(`formatClarificationAnswerComment: answer key "${key}" is not a positive integer`);
63
+ }
64
+ const value = marker.answers[n];
65
+ if (typeof value !== 'string' || value.length === 0) {
66
+ throw new Error(`formatClarificationAnswerComment: answer for Q${n} is empty`);
67
+ }
68
+ }
69
+ // Reference the shared pending literal so the import stays live (per
70
+ // data-model.md §"Cross-package import strategy" — divergence between the
71
+ // parser's canonical value and cockpit's rendered surface is structurally
72
+ // impossible). The stamped body never renders the literal itself; the tool
73
+ // refuses empty values above.
74
+ void PENDING_ANSWER_LITERAL;
75
+ }
76
+ //# sourceMappingURL=clarification-answer-marker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clarification-answer-marker.js","sourceRoot":"","sources":["../../../../src/cli/commands/cockpit/clarification-answer-marker.ts"],"names":[],"mappings":"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"}
@@ -1,13 +1,3 @@
1
- /**
2
- * `findClarificationComment` — implements AD-3 / D-R1:
3
- *
4
- * 1. Walk `gh api repos/{o}/{r}/issues/{n}/timeline` for the most-recent event
5
- * `event === 'labeled'` with `label.name === 'waiting-for:clarification'`.
6
- * 2. Take its `created_at` timestamp.
7
- * 3. Fetch `gh issue view --json comments` and return the first comment whose
8
- * `createdAt >= labelEventTs`.
9
- * 4. Return `null` if no qualifying comment exists.
10
- */
11
1
  import type { GhWrapper, IssueComment } from '@generacy-ai/cockpit';
12
2
  export declare function findClarificationComment(gh: GhWrapper, repo: string, number: number): Promise<IssueComment | null>;
13
3
  //# sourceMappingURL=clarification-comment-finder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"clarification-comment-finder.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/cockpit/clarification-comment-finder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAUpE,wBAAsB,wBAAwB,CAC5C,EAAE,EAAE,SAAS,EACb,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CA0B9B"}
1
+ {"version":3,"file":"clarification-comment-finder.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/cockpit/clarification-comment-finder.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAwCpE,wBAAsB,wBAAwB,CAC5C,EAAE,EAAE,SAAS,EACb,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAyC9B"}
@@ -1,5 +1,57 @@
1
+ /**
2
+ * `findClarificationComment` — marker-first / timeline-fallback strategy (#995).
3
+ *
4
+ * Primary path: return the latest-by-`createdAt` non-stage-status comment
5
+ * carrying a `CLARIFICATION_QUESTION_MARKERS` prefix at column 0. This survives
6
+ * `waiting-for:clarification` re-application (requeue / boot-resume / restart)
7
+ * that jumps the label timestamp past every question comment.
8
+ *
9
+ * Fallback path: today's most-recent-`labeled` + at-or-after scan, preserved
10
+ * verbatim for legacy marker-less batches. Emits one `warn` per invocation.
11
+ */
12
+ import { matchClarificationQuestionMarker } from '@generacy-ai/orchestrator';
13
+ import { getLogger } from '../../utils/logger.js';
1
14
  const WAITING_CLARIFICATION = 'waiting-for:clarification';
15
+ const STAGE_STATUS_REJECT_PREFIXES = [
16
+ '<!-- generacy-stage:planning',
17
+ '<!-- generacy-stage:specification',
18
+ '<!-- generacy-stage:implementation',
19
+ '<!-- speckit-stage:planning',
20
+ '<!-- speckit-stage:specification',
21
+ '<!-- speckit-stage:implementation',
22
+ ];
23
+ const CLARIFICATION_STAGE_OVERRIDE_PREFIXES = [
24
+ '<!-- generacy-stage:clarification',
25
+ '<!-- generacy-stage:clarification-batch-',
26
+ ];
27
+ function isStageStatusComment(body) {
28
+ const lines = body.split('\n');
29
+ for (const line of lines) {
30
+ for (const prefix of CLARIFICATION_STAGE_OVERRIDE_PREFIXES) {
31
+ if (line.startsWith(prefix))
32
+ return false;
33
+ }
34
+ }
35
+ for (const line of lines) {
36
+ for (const prefix of STAGE_STATUS_REJECT_PREFIXES) {
37
+ if (line.startsWith(prefix))
38
+ return true;
39
+ }
40
+ }
41
+ return false;
42
+ }
2
43
  export async function findClarificationComment(gh, repo, number) {
44
+ const comments = await gh.fetchIssueComments(repo, number);
45
+ // Marker-first pass: survives waiting-for:clarification re-application that
46
+ // would otherwise strand the question comment behind the label event.
47
+ const markerHits = comments
48
+ .filter((c) => matchClarificationQuestionMarker(c.body) !== undefined)
49
+ .filter((c) => !isStageStatusComment(c.body))
50
+ .sort((a, b) => Date.parse(b.createdAt) - Date.parse(a.createdAt));
51
+ if (markerHits.length > 0)
52
+ return markerHits[0] ?? null;
53
+ const [owner, repoName] = repo.split('/', 2);
54
+ getLogger().warn({ owner, repo: repoName, issue: number }, `marker-less clarification comment; poster should be updated — issue=${repo}#${number}`);
3
55
  const timeline = (await gh.fetchIssueTimeline(repo, number));
4
56
  let latestLabelTs = null;
5
57
  for (const event of timeline) {
@@ -18,13 +70,14 @@ export async function findClarificationComment(gh, repo, number) {
18
70
  const labelTime = Date.parse(latestLabelTs);
19
71
  if (Number.isNaN(labelTime))
20
72
  return null;
21
- const comments = await gh.fetchIssueComments(repo, number);
22
73
  const sorted = [...comments].sort((a, b) => Date.parse(a.createdAt) - Date.parse(b.createdAt));
23
74
  for (const c of sorted) {
24
75
  const ct = Date.parse(c.createdAt);
25
- if (!Number.isNaN(ct) && ct >= labelTime) {
26
- return c;
27
- }
76
+ if (Number.isNaN(ct) || ct < labelTime)
77
+ continue;
78
+ if (isStageStatusComment(c.body))
79
+ continue;
80
+ return c;
28
81
  }
29
82
  return null;
30
83
  }
@@ -1 +1 @@
1
- {"version":3,"file":"clarification-comment-finder.js","sourceRoot":"","sources":["../../../../src/cli/commands/cockpit/clarification-comment-finder.ts"],"names":[],"mappings":"AAYA,MAAM,qBAAqB,GAAG,2BAA2B,CAAC;AAQ1D,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,EAAa,EACb,IAAY,EACZ,MAAc;IAEd,MAAM,QAAQ,GAAG,CAAC,MAAM,EAAE,CAAC,kBAAkB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAyB,CAAC;IAErF,IAAI,aAAa,GAAkB,IAAI,CAAC;IACxC,KAAK,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;QAC7B,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;YAAE,SAAS;QACxC,IAAI,KAAK,CAAC,KAAK,EAAE,IAAI,KAAK,qBAAqB;YAAE,SAAS;QAC1D,IAAI,KAAK,CAAC,UAAU,IAAI,IAAI;YAAE,SAAS;QACvC,IAAI,aAAa,IAAI,IAAI,IAAI,KAAK,CAAC,UAAU,GAAG,aAAa,EAAE,CAAC;YAC9D,aAAa,GAAG,KAAK,CAAC,UAAU,CAAC;QACnC,CAAC;IACH,CAAC;IAED,IAAI,aAAa,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IACvC,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IAC5C,IAAI,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC;IAEzC,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,kBAAkB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC3D,MAAM,MAAM,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;IAC/F,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QACnC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,SAAS,EAAE,CAAC;YACzC,OAAO,CAAC,CAAC;QACX,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
1
+ {"version":3,"file":"clarification-comment-finder.js","sourceRoot":"","sources":["../../../../src/cli/commands/cockpit/clarification-comment-finder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * #958 — `runClarifyRelay` — deterministic answer-relay path for cockpit's
3
+ * clarify skill. Replaces the freehand `gh issue comment` invocation that
4
+ * caused #958's answer-side lottery (four different agent-invented markers
5
+ * observed on #5/#6/#7/#8 in one run).
6
+ *
7
+ * The tool posts a marker-stamped comment (via `formatClarificationAnswerComment`)
8
+ * and applies `completed:clarification`. Idempotent: a second call with the
9
+ * same batch is a no-op (`action: 'already-relayed'`).
10
+ *
11
+ * MUST NOT rewrite `clarifications.md` locally. MUST NOT remove
12
+ * `waiting-for:clarification` (the worker owns that on resume).
13
+ */
14
+ import { loadCockpitConfig, type CommandRunner, type GhWrapper } from '@generacy-ai/cockpit';
15
+ import { type IssueRef } from './resolver.js';
16
+ export interface ClarifyRelayInput {
17
+ issue: string;
18
+ batch: number;
19
+ answers: Record<number, string>;
20
+ actor?: string;
21
+ }
22
+ export interface ClarifyRelayResult {
23
+ ref: IssueRef;
24
+ batch: number;
25
+ action: 'relayed' | 'already-relayed';
26
+ completedLabel: 'completed:clarification';
27
+ commentUrl?: string;
28
+ noop?: true;
29
+ }
30
+ export interface ClarifyRelayDeps {
31
+ runner?: CommandRunner;
32
+ gh?: GhWrapper;
33
+ loadConfig?: typeof loadCockpitConfig;
34
+ env?: NodeJS.ProcessEnv;
35
+ now?: () => Date;
36
+ }
37
+ export declare function runClarifyRelay(input: ClarifyRelayInput, deps?: ClarifyRelayDeps): Promise<ClarifyRelayResult>;
38
+ //# sourceMappingURL=clarify-relay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clarify-relay.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/cockpit/clarify-relay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EACL,iBAAiB,EACjB,KAAK,aAAa,EAClB,KAAK,SAAS,EACf,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAuB,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AAKnE,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,QAAQ,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,SAAS,GAAG,iBAAiB,CAAC;IACtC,cAAc,EAAE,yBAAyB,CAAC;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,IAAI,CAAC;CACb;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,EAAE,CAAC,EAAE,SAAS,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,iBAAiB,CAAC;IACtC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AAiBD,wBAAsB,eAAe,CACnC,KAAK,EAAE,iBAAiB,EACxB,IAAI,GAAE,gBAAqB,GAC1B,OAAO,CAAC,kBAAkB,CAAC,CAkG7B"}
@@ -0,0 +1,120 @@
1
+ /**
2
+ * #958 — `runClarifyRelay` — deterministic answer-relay path for cockpit's
3
+ * clarify skill. Replaces the freehand `gh issue comment` invocation that
4
+ * caused #958's answer-side lottery (four different agent-invented markers
5
+ * observed on #5/#6/#7/#8 in one run).
6
+ *
7
+ * The tool posts a marker-stamped comment (via `formatClarificationAnswerComment`)
8
+ * and applies `completed:clarification`. Idempotent: a second call with the
9
+ * same batch is a no-op (`action: 'already-relayed'`).
10
+ *
11
+ * MUST NOT rewrite `clarifications.md` locally. MUST NOT remove
12
+ * `waiting-for:clarification` (the worker owns that on resume).
13
+ */
14
+ import { loadCockpitConfig, } from '@generacy-ai/cockpit';
15
+ import { getLogger } from '../../utils/logger.js';
16
+ import { resolveIssueContext } from './resolver.js';
17
+ import { CockpitExit } from './exit.js';
18
+ import { resolveCockpitIdentity } from './shared/identity.js';
19
+ import { formatClarificationAnswerComment } from './clarification-answer-marker.js';
20
+ const COMPLETED_LABEL = 'completed:clarification';
21
+ /**
22
+ * Detect a prior stamped answer comment for the same batch. The marker header
23
+ * carries the batch as the first attribute (`:<batch>`) — any comment whose
24
+ * body starts a line with `<!-- generacy-clarification-answers:<batch>` matches.
25
+ */
26
+ function hasPriorMarkerForBatch(body, batch) {
27
+ const prefix = `<!-- generacy-clarification-answers:${batch}`;
28
+ for (const line of body.split('\n')) {
29
+ if (line.startsWith(prefix))
30
+ return true;
31
+ }
32
+ return false;
33
+ }
34
+ export async function runClarifyRelay(input, deps = {}) {
35
+ const log = getLogger();
36
+ const loaded = await (deps.loadConfig ?? loadCockpitConfig)({});
37
+ for (const w of loaded.warnings)
38
+ log.warn(w);
39
+ let ref;
40
+ let gh;
41
+ try {
42
+ const resolvedCtx = await resolveIssueContext({
43
+ issue: input.issue,
44
+ ...(deps.runner != null ? { runner: deps.runner } : {}),
45
+ });
46
+ ref = resolvedCtx.ref;
47
+ gh = deps.gh ?? resolvedCtx.gh;
48
+ }
49
+ catch (err) {
50
+ throw new CockpitExit(2, `Error: cockpit clarify-relay: ${err.message}`);
51
+ }
52
+ // Resolve actor (optional). An unresolvable identity leaves the actor attr
53
+ // off the marker header — the marker match rule is prefix-based so this is
54
+ // a cosmetic degradation, not a functional one.
55
+ const identityInput = {
56
+ flag: undefined,
57
+ configAssignee: loaded.config.assignee,
58
+ gh,
59
+ logger: log,
60
+ verb: 'clarify-relay',
61
+ mode: 'optional',
62
+ ...(deps.env != null ? { env: deps.env } : {}),
63
+ };
64
+ const identity = await resolveCockpitIdentity(identityInput);
65
+ const actor = input.actor ?? identity.login;
66
+ // Idempotence — a comment carrying this batch's marker means we already
67
+ // relayed. Return no-op without re-posting or re-labelling.
68
+ let existingComments;
69
+ try {
70
+ existingComments = await gh.fetchIssueComments(ref.nwo, ref.number);
71
+ }
72
+ catch (err) {
73
+ throw new CockpitExit(1, `Error: cockpit clarify-relay: gh issue view (fetch comments): ${err.message}`);
74
+ }
75
+ const alreadyRelayed = existingComments.some((c) => hasPriorMarkerForBatch(c.body, input.batch));
76
+ if (alreadyRelayed) {
77
+ return {
78
+ ref,
79
+ batch: input.batch,
80
+ action: 'already-relayed',
81
+ completedLabel: COMPLETED_LABEL,
82
+ noop: true,
83
+ };
84
+ }
85
+ const ts = (deps.now ?? (() => new Date()))().toISOString();
86
+ let body;
87
+ try {
88
+ body = formatClarificationAnswerComment({
89
+ batch: input.batch,
90
+ answers: input.answers,
91
+ ...(actor != null ? { actor } : {}),
92
+ ts,
93
+ });
94
+ }
95
+ catch (err) {
96
+ throw new CockpitExit(2, `Error: cockpit clarify-relay: ${err.message}`);
97
+ }
98
+ let commentUrl;
99
+ try {
100
+ commentUrl = (await gh.postIssueComment(ref.nwo, ref.number, body)).url;
101
+ }
102
+ catch (err) {
103
+ throw new CockpitExit(1, `Error: cockpit clarify-relay: gh issue comment: ${err.message}`);
104
+ }
105
+ try {
106
+ await gh.addLabel(ref.nwo, ref.number, COMPLETED_LABEL);
107
+ }
108
+ catch (err) {
109
+ throw new CockpitExit(1, `Error: cockpit clarify-relay: gh issue edit (add ${COMPLETED_LABEL}): ${err.message}` +
110
+ ` (comment posted: ${commentUrl})`);
111
+ }
112
+ return {
113
+ ref,
114
+ batch: input.batch,
115
+ action: 'relayed',
116
+ completedLabel: COMPLETED_LABEL,
117
+ commentUrl,
118
+ };
119
+ }
120
+ //# sourceMappingURL=clarify-relay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clarify-relay.js","sourceRoot":"","sources":["../../../../src/cli/commands/cockpit/clarify-relay.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * `maybeRefreshAggregate` — Q4=A on-demand aggregate refresh. Recomputes
3
+ * `phase-complete` / `epic-complete` events only when the current webhook
4
+ * payload could plausibly change an aggregate state (completed:* label,
5
+ * issue.closed, pull_request.closed). Zero GraphQL cost on non-completion
6
+ * payloads.
7
+ *
8
+ * Contract: `specs/978-summary-generacy-cockpit/contracts/aggregate-on-demand.md`.
9
+ */
10
+ import { type GhWrapper, type ResolvedEpic } from '@generacy-ai/cockpit';
11
+ import { type AggregateState } from '../watch/aggregate.js';
12
+ import type { AggregateEvent } from '../watch/aggregate-emit.js';
13
+ import type { SnapshotMap } from '../watch/snapshot.js';
14
+ export type AggregateTrigger = {
15
+ kind: 'completed-label';
16
+ label: string;
17
+ } | {
18
+ kind: 'issue-closed';
19
+ } | {
20
+ kind: 'pr-closed';
21
+ } | null;
22
+ export interface AggregateRefreshInput {
23
+ trigger: AggregateTrigger;
24
+ epicRef: string;
25
+ epicRepo: string;
26
+ epicNumber: number;
27
+ prevAgg: AggregateState;
28
+ prev: SnapshotMap;
29
+ currentResolved: ResolvedEpic | null;
30
+ gh: GhWrapper;
31
+ logger: {
32
+ warn: (msg: string) => void;
33
+ };
34
+ now: () => string;
35
+ }
36
+ export interface AggregateRefreshOutput {
37
+ events: AggregateEvent[];
38
+ nextAgg: AggregateState;
39
+ nextPrev: SnapshotMap;
40
+ nextResolved: ResolvedEpic | null;
41
+ }
42
+ export declare function maybeRefreshAggregate(input: AggregateRefreshInput): Promise<AggregateRefreshOutput>;
43
+ //# sourceMappingURL=aggregate-on-demand.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aggregate-on-demand.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/cockpit/doorbell/aggregate-on-demand.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAe,KAAK,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEtF,OAAO,EAA0B,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACpF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,MAAM,MAAM,gBAAgB,GACxB;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,GACxB;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,GACrB,IAAI,CAAC;AAET,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,gBAAgB,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,cAAc,CAAC;IACxB,IAAI,EAAE,WAAW,CAAC;IAClB,eAAe,EAAE,YAAY,GAAG,IAAI,CAAC;IACrC,EAAE,EAAE,SAAS,CAAC;IACd,MAAM,EAAE;QAAE,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;IACxC,GAAG,EAAE,MAAM,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,OAAO,EAAE,cAAc,CAAC;IACxB,QAAQ,EAAE,WAAW,CAAC;IACtB,YAAY,EAAE,YAAY,GAAG,IAAI,CAAC;CACnC;AAWD,wBAAsB,qBAAqB,CACzC,KAAK,EAAE,qBAAqB,GAC3B,OAAO,CAAC,sBAAsB,CAAC,CA2DjC"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * `maybeRefreshAggregate` — Q4=A on-demand aggregate refresh. Recomputes
3
+ * `phase-complete` / `epic-complete` events only when the current webhook
4
+ * payload could plausibly change an aggregate state (completed:* label,
5
+ * issue.closed, pull_request.closed). Zero GraphQL cost on non-completion
6
+ * payloads.
7
+ *
8
+ * Contract: `specs/978-summary-generacy-cockpit/contracts/aggregate-on-demand.md`.
9
+ */
10
+ import { resolveEpic } from '@generacy-ai/cockpit';
11
+ import { runOnePoll } from '../watch/poll-loop.js';
12
+ import { computeAggregateEvents } from '../watch/aggregate.js';
13
+ function identity(input) {
14
+ return {
15
+ events: [],
16
+ nextAgg: input.prevAgg,
17
+ nextPrev: input.prev,
18
+ nextResolved: input.currentResolved,
19
+ };
20
+ }
21
+ export async function maybeRefreshAggregate(input) {
22
+ if (input.trigger == null)
23
+ return identity(input);
24
+ let resolved = input.currentResolved;
25
+ if (resolved == null) {
26
+ try {
27
+ resolved = await resolveEpic({
28
+ epicRef: input.epicRef,
29
+ gh: input.gh,
30
+ logger: input.logger,
31
+ });
32
+ }
33
+ catch (err) {
34
+ input.logger.warn(`cockpit doorbell: aggregate resolveEpic failed: ${err instanceof Error ? err.message : String(err)}`);
35
+ return identity(input);
36
+ }
37
+ }
38
+ let pollResult;
39
+ try {
40
+ pollResult = await runOnePoll(input.prev, {
41
+ gh: input.gh,
42
+ refs: resolved.parsed.allRefs,
43
+ epicOwnerRepo: resolved.epic.repo,
44
+ logger: input.logger,
45
+ });
46
+ }
47
+ catch (err) {
48
+ input.logger.warn(`cockpit doorbell: aggregate runOnePoll failed: ${err instanceof Error ? err.message : String(err)}`);
49
+ return {
50
+ events: [],
51
+ nextAgg: input.prevAgg,
52
+ nextPrev: input.prev,
53
+ nextResolved: resolved,
54
+ };
55
+ }
56
+ const aggregate = computeAggregateEvents({
57
+ curr: pollResult.curr,
58
+ parsed: resolved.parsed,
59
+ epicRepo: input.epicRepo,
60
+ epicNumber: input.epicNumber,
61
+ prevState: input.prevAgg,
62
+ initial: false,
63
+ now: input.now,
64
+ });
65
+ return {
66
+ events: aggregate.events,
67
+ nextAgg: aggregate.nextState,
68
+ nextPrev: pollResult.curr,
69
+ nextResolved: resolved,
70
+ };
71
+ }
72
+ //# sourceMappingURL=aggregate-on-demand.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aggregate-on-demand.js","sourceRoot":"","sources":["../../../../../src/cli/commands/cockpit/doorbell/aggregate-on-demand.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,WAAW,EAAqC,MAAM,sBAAsB,CAAC;AACtF,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,sBAAsB,EAAuB,MAAM,uBAAuB,CAAC;AA8BpF,SAAS,QAAQ,CAAC,KAA4B;IAC5C,OAAO;QACL,MAAM,EAAE,EAAE;QACV,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,QAAQ,EAAE,KAAK,CAAC,IAAI;QACpB,YAAY,EAAE,KAAK,CAAC,eAAe;KACpC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,KAA4B;IAE5B,IAAI,KAAK,CAAC,OAAO,IAAI,IAAI;QAAE,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC;IAElD,IAAI,QAAQ,GAAwB,KAAK,CAAC,eAAe,CAAC;IAC1D,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;QACrB,IAAI,CAAC;YACH,QAAQ,GAAG,MAAM,WAAW,CAAC;gBAC3B,OAAO,EAAE,KAAK,CAAC,OAAO;gBACtB,EAAE,EAAE,KAAK,CAAC,EAAE;gBACZ,MAAM,EAAE,KAAK,CAAC,MAAM;aACrB,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,KAAK,CAAC,MAAM,CAAC,IAAI,CACf,mDACE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CACjD,EAAE,CACH,CAAC;YACF,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IAED,IAAI,UAAiC,CAAC;IACtC,IAAI,CAAC;QACH,UAAU,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,IAAI,EAAE;YACxC,EAAE,EAAE,KAAK,CAAC,EAAE;YACZ,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,OAAO;YAC7B,aAAa,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI;YACjC,MAAM,EAAE,KAAK,CAAC,MAAM;SACrB,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,KAAK,CAAC,MAAM,CAAC,IAAI,CACf,kDACE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CACjD,EAAE,CACH,CAAC;QACF,OAAO;YACL,MAAM,EAAE,EAAE;YACV,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,QAAQ,EAAE,KAAK,CAAC,IAAI;YACpB,YAAY,EAAE,QAAQ;SACvB,CAAC;IACJ,CAAC;IAED,MAAM,SAAS,GAAG,sBAAsB,CAAC;QACvC,IAAI,EAAE,UAAU,CAAC,IAAI;QACrB,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,SAAS,EAAE,KAAK,CAAC,OAAO;QACxB,OAAO,EAAE,KAAK;QACd,GAAG,EAAE,KAAK,CAAC,GAAG;KACf,CAAC,CAAC;IAEH,OAAO;QACL,MAAM,EAAE,SAAS,CAAC,MAAM;QACxB,OAAO,EAAE,SAAS,CAAC,SAAS;QAC5B,QAAQ,EAAE,UAAU,CAAC,IAAI;QACzB,YAAY,EAAE,QAAQ;KACvB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,103 @@
1
+ /**
2
+ * `discoverChannelUrl` — resolves the smee.io channel URL for the doorbell to
3
+ * consume. Five-stage lookup: env override → webhook-config (via `gh api
4
+ * /hooks`) → workspace walk-up → absolute workspace-mirror path →
5
+ * cluster-internal fallback. Never throws; malformed input logs a warning and
6
+ * returns null.
7
+ *
8
+ * Contract: `specs/988-summary-cockpit-auto-doorbell/contracts/channel-discovery.md`.
9
+ */
10
+ import type { PathLike } from 'node:fs';
11
+ import { z } from 'zod';
12
+ import type { CommandRunner } from '@generacy-ai/cockpit';
13
+ export declare const DEFAULT_CHANNEL_FILE_PATH = "/var/lib/generacy/smee-channel";
14
+ export declare const DEFAULT_WORKSPACE_MIRROR_PATH = "/workspaces/.generacy/cockpit/smee-channel";
15
+ export declare const DEFAULT_WEBHOOK_CONFIG_TIMEOUT_MS = 5000;
16
+ export declare const SMEE_URL_PATTERN: RegExp;
17
+ export type ChannelSource = 'env' | 'webhook-config' | 'workspace-walkup' | 'workspace-absolute' | 'file';
18
+ export interface ChannelDiscoveryResult {
19
+ url: string;
20
+ source: ChannelSource;
21
+ }
22
+ interface ReadFileFn {
23
+ (path: PathLike, encoding: BufferEncoding): Promise<string>;
24
+ }
25
+ export interface ChannelDiscoveryInput {
26
+ env: NodeJS.ProcessEnv;
27
+ channelFilePath: string;
28
+ fs: {
29
+ readFile: ReadFileFn;
30
+ };
31
+ logger?: {
32
+ warn?: (msg: string) => void;
33
+ };
34
+ /** Starting directory for walk-up scan. Defaults to `process.cwd()`. */
35
+ cwd?: string;
36
+ /** Absolute fallback if walk-up produces no hit. */
37
+ workspaceMirrorPath?: string;
38
+ /**
39
+ * Pre-parsed target repos for the webhook-config stage, primary-first.
40
+ * The caller (`doorbell.ts`) derives this via `resolveWebhookTargets`.
41
+ * When absent or empty, the webhook-config stage is skipped.
42
+ */
43
+ targets?: Array<{
44
+ owner: string;
45
+ repo: string;
46
+ }>;
47
+ /**
48
+ * Command runner used to invoke `gh api …/hooks`. When absent, the
49
+ * webhook-config stage is skipped even if `targets` is non-empty.
50
+ */
51
+ runner?: CommandRunner;
52
+ /**
53
+ * Per-call timeout for the `gh api …/hooks` invocation. Default 5000ms
54
+ * (spec FR-009). Exposed for tests.
55
+ */
56
+ webhookConfigTimeoutMs?: number;
57
+ }
58
+ export declare const SmeeHookSchema: z.ZodObject<{
59
+ id: z.ZodNumber;
60
+ active: z.ZodBoolean;
61
+ config: z.ZodObject<{
62
+ url: z.ZodString;
63
+ }, "strip", z.ZodTypeAny, {
64
+ url: string;
65
+ }, {
66
+ url: string;
67
+ }>;
68
+ updated_at: z.ZodString;
69
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
70
+ id: z.ZodNumber;
71
+ active: z.ZodBoolean;
72
+ config: z.ZodObject<{
73
+ url: z.ZodString;
74
+ }, "strip", z.ZodTypeAny, {
75
+ url: string;
76
+ }, {
77
+ url: string;
78
+ }>;
79
+ updated_at: z.ZodString;
80
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
81
+ id: z.ZodNumber;
82
+ active: z.ZodBoolean;
83
+ config: z.ZodObject<{
84
+ url: z.ZodString;
85
+ }, "strip", z.ZodTypeAny, {
86
+ url: string;
87
+ }, {
88
+ url: string;
89
+ }>;
90
+ updated_at: z.ZodString;
91
+ }, z.ZodTypeAny, "passthrough">>;
92
+ export type SmeeHook = z.infer<typeof SmeeHookSchema>;
93
+ /**
94
+ * Pure tie-break for `/hooks` payloads (FR-005).
95
+ * 1. Keep only `active === true`.
96
+ * 2. Keep only entries whose `config.url` matches `SMEE_URL_PATTERN`.
97
+ * 3. Sort by `Date.parse(updated_at)` desc; `NaN` sorts last (`-Infinity`).
98
+ * 4. Return the first entry or `null`.
99
+ */
100
+ export declare function pickSmeeHook(hooks: SmeeHook[]): SmeeHook | null;
101
+ export declare function discoverChannelUrl(input: ChannelDiscoveryInput): Promise<ChannelDiscoveryResult | null>;
102
+ export {};
103
+ //# sourceMappingURL=channel-discovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel-discovery.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/cockpit/doorbell/channel-discovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAExC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D,eAAO,MAAM,yBAAyB,mCAAmC,CAAC;AAC1E,eAAO,MAAM,6BAA6B,+CACI,CAAC;AAC/C,eAAO,MAAM,iCAAiC,OAAQ,CAAC;AAIvD,eAAO,MAAM,gBAAgB,QAAyC,CAAC;AAEvE,MAAM,MAAM,aAAa,GACrB,KAAK,GACL,gBAAgB,GAChB,kBAAkB,GAClB,oBAAoB,GACpB,MAAM,CAAC;AAEX,MAAM,WAAW,sBAAsB;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,aAAa,CAAC;CACvB;AAED,UAAU,UAAU;IAClB,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC7D;AAED,MAAM,WAAW,qBAAqB;IACpC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,EAAE,EAAE;QAAE,QAAQ,EAAE,UAAU,CAAA;KAAE,CAAC;IAC7B,MAAM,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;IAC1C,wEAAwE;IACxE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,oDAAoD;IACpD,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;OAIG;IACH,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjD;;;OAGG;IACH,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB;;;OAGG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gCAOX,CAAC;AAEjB,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAEtD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,QAAQ,GAAG,IAAI,CAU/D;AAoGD,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,qBAAqB,GAC3B,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAAC,CA0FxC"}