@happyvertical/repos 0.85.5 → 0.86.1
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/AGENT.md +1 -1
- package/README.md +21 -0
- package/dist/index.d.ts +231 -0
- package/dist/index.js +6742 -5
- package/dist/index.js.map +1 -1
- package/metadata.json +2 -1
- package/package.json +3 -2
package/AGENT.md
CHANGED
|
@@ -27,7 +27,7 @@ pnpm --filter @happyvertical/repos clean
|
|
|
27
27
|
## Ecosystem Relationships
|
|
28
28
|
- Provides: Standardized repository interface for GitHub, GitLab, Bitbucket, and Azure DevOps
|
|
29
29
|
- Implements: none
|
|
30
|
-
- Requires: @happyvertical/graphql, js-yaml
|
|
30
|
+
- Requires: @happyvertical/graphql, js-yaml, nostr-tools
|
|
31
31
|
- Stability: stable (Primary package surface is described as implemented and production-oriented.)
|
|
32
32
|
<!-- END AGENT:GENERATED -->
|
|
33
33
|
|
package/README.md
CHANGED
|
@@ -123,6 +123,27 @@ an observation. The exported `createGitHubWebhookFixture()` helper creates exact
|
|
|
123
123
|
deterministic bytes and signatures for duplicate, redelivery, delayed, and
|
|
124
124
|
out-of-order integration scenarios.
|
|
125
125
|
|
|
126
|
+
### Buzz forge relay
|
|
127
|
+
|
|
128
|
+
`BuzzRelayClient` polls configured Buzz/Nostr relays and normalizes supported
|
|
129
|
+
forge kinds into provider-neutral `ForgeEventEnvelope` values. Production
|
|
130
|
+
events are verified with `nostr-tools` before normalization; the
|
|
131
|
+
`allowUnverifiedFixtures` option is solely for deterministic test fixtures.
|
|
132
|
+
When `channelIds` is set, accepted events must have a matching `channel` or
|
|
133
|
+
`h` tag. Kind-7 approvals need pull-request metadata or their referenced
|
|
134
|
+
kind-1617 patch; pass a kind-39002 members event and `roleFloor` to enforce
|
|
135
|
+
channel roles.
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
import { BuzzRelayClient } from '@happyvertical/repos';
|
|
139
|
+
|
|
140
|
+
const buzz = new BuzzRelayClient({
|
|
141
|
+
relays: ['https://relay.example/buzz'],
|
|
142
|
+
channelIds: ['channel-hv'],
|
|
143
|
+
});
|
|
144
|
+
const events = await buzz.pollOnce();
|
|
145
|
+
```
|
|
146
|
+
|
|
126
147
|
### Errors and provider metadata
|
|
127
148
|
|
|
128
149
|
New forge APIs throw `ForgeError`, which exposes `code`, `provider`, `status`,
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,119 @@ export declare interface Branch {
|
|
|
4
4
|
protected: boolean;
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
+
export declare const BUZZ_FIXTURE_PUBKEYS: {
|
|
8
|
+
readonly owner: "2ad53df8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa4f05";
|
|
9
|
+
readonly attestor: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
|
|
10
|
+
readonly reactor: "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc";
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Buzz/Nostr forge event kinds verified live against desktop-v0.5.3 (ADR-002).
|
|
15
|
+
* Kind numbers are load-bearing and must match the deployed relay.
|
|
16
|
+
*/
|
|
17
|
+
export declare const BUZZ_FORGE_KINDS: {
|
|
18
|
+
readonly repositoryAnnouncement: 30617;
|
|
19
|
+
readonly refUpdate: 30618;
|
|
20
|
+
readonly patch: 1617;
|
|
21
|
+
readonly statusOpen: 1630;
|
|
22
|
+
readonly statusApplied: 1631;
|
|
23
|
+
readonly statusClosed: 1632;
|
|
24
|
+
readonly statusDraft: 1633;
|
|
25
|
+
readonly reaction: 7;
|
|
26
|
+
readonly channelMembers: 39002;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/** Channel role floor used when validating kind:7 approvals (ADR-002). */
|
|
30
|
+
export declare type BuzzChannelRole = 'owner' | 'admin' | 'member' | 'guest' | 'bot';
|
|
31
|
+
|
|
32
|
+
declare interface BuzzFixtureOptions {
|
|
33
|
+
/** Override event id after computing the NIP-01 digest (duplicate/replay cases). */
|
|
34
|
+
id?: string;
|
|
35
|
+
pubkey?: string;
|
|
36
|
+
created_at?: number;
|
|
37
|
+
tags?: readonly (readonly string[])[];
|
|
38
|
+
content?: string;
|
|
39
|
+
sig?: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export declare type BuzzForgeKind = (typeof BUZZ_FORGE_KINDS)[keyof typeof BUZZ_FORGE_KINDS];
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Minimal buzz relay client: HTTP REQ polling plus an injectable event source
|
|
46
|
+
* for tests and WebSocket adapters.
|
|
47
|
+
*
|
|
48
|
+
* The client never mutates process-global state. Each subscribe/poll call is
|
|
49
|
+
* scoped to the constructed options.
|
|
50
|
+
*/
|
|
51
|
+
export declare class BuzzRelayClient {
|
|
52
|
+
private readonly relays;
|
|
53
|
+
private readonly kinds;
|
|
54
|
+
private readonly channelIds;
|
|
55
|
+
private readonly fetchImpl;
|
|
56
|
+
private readonly pollIntervalMs;
|
|
57
|
+
private readonly seenIds;
|
|
58
|
+
constructor(options: BuzzRelayClientOptions);
|
|
59
|
+
/**
|
|
60
|
+
* Poll every configured relay once with a NIP-01-style HTTP REQ body and
|
|
61
|
+
* return newly observed, verified envelopes (deduped by event id).
|
|
62
|
+
*/
|
|
63
|
+
pollOnce(options?: {
|
|
64
|
+
since?: number;
|
|
65
|
+
allowUnverifiedFixtures?: boolean;
|
|
66
|
+
membersEvent?: NostrForgeEvent;
|
|
67
|
+
referencedPatchEvent?: NostrForgeEvent;
|
|
68
|
+
}): Promise<ForgeEventEnvelope[]>;
|
|
69
|
+
/**
|
|
70
|
+
* Poll on an interval until `close()` is called. Useful for long-running
|
|
71
|
+
* Aedile adapters. Returns a subscription handle.
|
|
72
|
+
*/
|
|
73
|
+
subscribe(onEvent: (envelope: ForgeEventEnvelope) => void | Promise<void>, options?: {
|
|
74
|
+
since?: number;
|
|
75
|
+
allowUnverifiedFixtures?: boolean;
|
|
76
|
+
membersEvent?: NostrForgeEvent;
|
|
77
|
+
referencedPatchEvent?: NostrForgeEvent;
|
|
78
|
+
onError?: (error: unknown) => void;
|
|
79
|
+
}): BuzzRelaySubscription;
|
|
80
|
+
/**
|
|
81
|
+
* Normalize a pre-fetched event list (tests / custom transports) without
|
|
82
|
+
* contacting a relay. Applies the same kind/channel filters and id dedupe.
|
|
83
|
+
*/
|
|
84
|
+
ingestEvents(events: readonly NostrForgeEvent[], options?: {
|
|
85
|
+
allowUnverifiedFixtures?: boolean;
|
|
86
|
+
membersEvent?: NostrForgeEvent;
|
|
87
|
+
referencedPatchEvent?: NostrForgeEvent;
|
|
88
|
+
verify?: boolean;
|
|
89
|
+
}): ForgeEventEnvelope[];
|
|
90
|
+
/** Forget delivered ids (operator replay). */
|
|
91
|
+
resetSeen(): void;
|
|
92
|
+
private fetchEvents;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export declare interface BuzzRelayClientOptions {
|
|
96
|
+
/** WebSocket or HTTP relay endpoints. */
|
|
97
|
+
relays: readonly string[];
|
|
98
|
+
/** Optional filter limiting which kinds are accepted. */
|
|
99
|
+
kinds?: readonly number[];
|
|
100
|
+
/** Bound channel ids watched for forge activity. */
|
|
101
|
+
channelIds?: readonly string[];
|
|
102
|
+
/** Fetch implementation override for tests. */
|
|
103
|
+
fetch?: typeof fetch;
|
|
104
|
+
/** Poll interval in ms when using HTTP REQ polling. Default 5000. */
|
|
105
|
+
pollIntervalMs?: number;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export declare interface BuzzRelaySubscription {
|
|
109
|
+
close(): void;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export declare interface BuzzRoleResolution {
|
|
113
|
+
pubkey: string;
|
|
114
|
+
role: BuzzChannelRole;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Extract a channel id from common buzz tags when present. */
|
|
118
|
+
export declare function channelIdFromEvent(event: NostrForgeEvent): string | undefined;
|
|
119
|
+
|
|
7
120
|
/** Normalized provider check run. */
|
|
8
121
|
export declare interface CheckRun {
|
|
9
122
|
id: string;
|
|
@@ -47,6 +160,37 @@ export declare interface CommitStatus {
|
|
|
47
160
|
raw?: unknown;
|
|
48
161
|
}
|
|
49
162
|
|
|
163
|
+
/**
|
|
164
|
+
* Compute the NIP-01 event id for a Nostr event (sha256 of serialized fields).
|
|
165
|
+
* Used by fixtures and verification when the caller supplies an unsigned skeleton.
|
|
166
|
+
*/
|
|
167
|
+
export declare function computeNostrEventId(event: Omit<NostrForgeEvent, 'id' | 'sig'>): string;
|
|
168
|
+
|
|
169
|
+
export declare function createApprovalFixture(options?: BuzzFixtureOptions & {
|
|
170
|
+
content?: string;
|
|
171
|
+
headSha?: string;
|
|
172
|
+
targetEventId?: string;
|
|
173
|
+
}): NostrForgeEvent;
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Deterministic sequences for convergence suites (duplicate / delayed /
|
|
177
|
+
* out-of-order / replay). Each sequence is an ordered list of Nostr events;
|
|
178
|
+
* consumers may reshuffle delivery while asserting identical projection state.
|
|
179
|
+
*/
|
|
180
|
+
export declare function createBuzzConvergenceSequences(): {
|
|
181
|
+
canonical: NostrForgeEvent[];
|
|
182
|
+
duplicate: NostrForgeEvent[];
|
|
183
|
+
delayed: NostrForgeEvent[];
|
|
184
|
+
outOfOrder: NostrForgeEvent[];
|
|
185
|
+
replay: NostrForgeEvent[];
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Build a deterministic unsigned-then-id-bound Nostr event for tests.
|
|
190
|
+
* Signature bytes are placeholders; pair with `allowUnverifiedFixtures: true`.
|
|
191
|
+
*/
|
|
192
|
+
export declare function createBuzzFixtureEvent(kind: number, options?: BuzzFixtureOptions): NostrForgeEvent;
|
|
193
|
+
|
|
50
194
|
/** Input for publishing a provider check run. */
|
|
51
195
|
export declare interface CreateCheckRunInput {
|
|
52
196
|
name: string;
|
|
@@ -115,6 +259,19 @@ export declare interface CreateIssueInput {
|
|
|
115
259
|
assignees?: string[];
|
|
116
260
|
}
|
|
117
261
|
|
|
262
|
+
export declare function createMembersFixture(options?: BuzzFixtureOptions & {
|
|
263
|
+
members?: readonly {
|
|
264
|
+
pubkey: string;
|
|
265
|
+
role: string;
|
|
266
|
+
}[];
|
|
267
|
+
}): NostrForgeEvent;
|
|
268
|
+
|
|
269
|
+
export declare function createPatchFixture(options?: BuzzFixtureOptions & {
|
|
270
|
+
headSha?: string;
|
|
271
|
+
number?: number;
|
|
272
|
+
repoId?: string;
|
|
273
|
+
}): NostrForgeEvent;
|
|
274
|
+
|
|
118
275
|
export declare interface CreatePRInput {
|
|
119
276
|
title: string;
|
|
120
277
|
body?: string;
|
|
@@ -123,6 +280,27 @@ export declare interface CreatePRInput {
|
|
|
123
280
|
draft?: boolean;
|
|
124
281
|
}
|
|
125
282
|
|
|
283
|
+
export declare function createRefUpdateFixture(options?: BuzzFixtureOptions & {
|
|
284
|
+
ref?: string;
|
|
285
|
+
head?: string;
|
|
286
|
+
previous?: string;
|
|
287
|
+
repoId?: string;
|
|
288
|
+
owner?: string;
|
|
289
|
+
}): NostrForgeEvent;
|
|
290
|
+
|
|
291
|
+
export declare function createRepositoryAnnouncementFixture(options?: BuzzFixtureOptions & {
|
|
292
|
+
owner?: string;
|
|
293
|
+
repoId?: string;
|
|
294
|
+
channelId?: string;
|
|
295
|
+
}): NostrForgeEvent;
|
|
296
|
+
|
|
297
|
+
export declare function createStatusFixture(options?: BuzzFixtureOptions & {
|
|
298
|
+
kind?: typeof BUZZ_FORGE_KINDS.statusOpen | typeof BUZZ_FORGE_KINDS.statusApplied | typeof BUZZ_FORGE_KINDS.statusClosed | typeof BUZZ_FORGE_KINDS.statusDraft;
|
|
299
|
+
context?: string;
|
|
300
|
+
headSha?: string;
|
|
301
|
+
conclusion?: string;
|
|
302
|
+
}): NostrForgeEvent;
|
|
303
|
+
|
|
126
304
|
/**
|
|
127
305
|
* Detect which template an issue was created from based on labels
|
|
128
306
|
*
|
|
@@ -884,6 +1062,17 @@ export declare function loadIssueTemplate(yamlPath: string): Promise<IssueTempla
|
|
|
884
1062
|
|
|
885
1063
|
export declare type MergeMethod = 'merge' | 'squash' | 'rebase';
|
|
886
1064
|
|
|
1065
|
+
/**
|
|
1066
|
+
* Normalize one already-verified Nostr forge event into a provider-neutral
|
|
1067
|
+
* envelope. Signature verification is the caller's responsibility (or use
|
|
1068
|
+
* {@link verifyAndNormalizeBuzzEvent}).
|
|
1069
|
+
*/
|
|
1070
|
+
export declare function normalizeBuzzEvent(event: NostrForgeEvent, receivedAt?: Date, options?: {
|
|
1071
|
+
roleFloor?: BuzzChannelRole;
|
|
1072
|
+
membersEvent?: NostrForgeEvent;
|
|
1073
|
+
referencedPatchEvent?: NostrForgeEvent;
|
|
1074
|
+
}): ForgeEventEnvelope;
|
|
1075
|
+
|
|
887
1076
|
/**
|
|
888
1077
|
* Normalizes one already-verified GitHub payload.
|
|
889
1078
|
* @param deliveryId Stable provider delivery identity.
|
|
@@ -894,6 +1083,17 @@ export declare type MergeMethod = 'merge' | 'squash' | 'rebase';
|
|
|
894
1083
|
*/
|
|
895
1084
|
export declare function normalizeGitHubWebhook(deliveryId: string, event: string, raw: unknown, receivedAt?: Date): ForgeEventEnvelope;
|
|
896
1085
|
|
|
1086
|
+
/** Minimal Nostr event shape consumed by the buzz forge adapter. */
|
|
1087
|
+
export declare interface NostrForgeEvent {
|
|
1088
|
+
id: string;
|
|
1089
|
+
pubkey: string;
|
|
1090
|
+
created_at: number;
|
|
1091
|
+
kind: number;
|
|
1092
|
+
tags: readonly (readonly string[])[];
|
|
1093
|
+
content: string;
|
|
1094
|
+
sig: string;
|
|
1095
|
+
}
|
|
1096
|
+
|
|
897
1097
|
/**
|
|
898
1098
|
* Parse an issue body into field values
|
|
899
1099
|
*
|
|
@@ -1027,6 +1227,15 @@ export declare enum RepositoryErrorCode {
|
|
|
1027
1227
|
UNKNOWN = "UNKNOWN"
|
|
1028
1228
|
}
|
|
1029
1229
|
|
|
1230
|
+
/**
|
|
1231
|
+
* Resolve a reactor's channel role from a kind:39002 members list.
|
|
1232
|
+
* Returns null when the pubkey is absent from the membership event.
|
|
1233
|
+
*/
|
|
1234
|
+
export declare function resolveBuzzChannelRole(membersEvent: NostrForgeEvent, pubkey: string): BuzzRoleResolution | null;
|
|
1235
|
+
|
|
1236
|
+
/** True when the reactor's role rank is at or above the configured floor. */
|
|
1237
|
+
export declare function roleMeetsFloor(role: BuzzChannelRole, floor: BuzzChannelRole): boolean;
|
|
1238
|
+
|
|
1030
1239
|
export declare interface SearchFilters {
|
|
1031
1240
|
state?: 'open' | 'closed' | 'all';
|
|
1032
1241
|
labels?: string[];
|
|
@@ -1088,4 +1297,26 @@ export declare interface User {
|
|
|
1088
1297
|
url?: string;
|
|
1089
1298
|
}
|
|
1090
1299
|
|
|
1300
|
+
/**
|
|
1301
|
+
* Verify then normalize a buzz forge event. Rejects invalid signatures without
|
|
1302
|
+
* producing an observation.
|
|
1303
|
+
*/
|
|
1304
|
+
export declare function verifyAndNormalizeBuzzEvent(event: NostrForgeEvent, receivedAt?: Date, options?: {
|
|
1305
|
+
roleFloor?: BuzzChannelRole;
|
|
1306
|
+
membersEvent?: NostrForgeEvent;
|
|
1307
|
+
referencedPatchEvent?: NostrForgeEvent;
|
|
1308
|
+
allowUnverifiedFixtures?: boolean;
|
|
1309
|
+
}): ForgeEventEnvelope;
|
|
1310
|
+
|
|
1311
|
+
/**
|
|
1312
|
+
* Structural signature verification for Nostr events.
|
|
1313
|
+
*
|
|
1314
|
+
* When `nostr-tools` is available it performs full Schnorr verification.
|
|
1315
|
+
* Otherwise it fails closed unless `allowUnverifiedFixtures` is set (tests
|
|
1316
|
+
* that inject pre-trusted fixture events with matching id digests).
|
|
1317
|
+
*/
|
|
1318
|
+
export declare function verifyNostrEventSignature(event: NostrForgeEvent, options?: {
|
|
1319
|
+
allowUnverifiedFixtures?: boolean;
|
|
1320
|
+
}): void;
|
|
1321
|
+
|
|
1091
1322
|
export { }
|