@roubo/plugin-sdk 0.1.0 → 0.1.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/dist/component-host-client.d.ts +33 -0
- package/dist/component-host-client.d.ts.map +1 -0
- package/dist/component-host-client.js +168 -0
- package/dist/component-host-client.js.map +1 -0
- package/dist/connection.d.ts +20 -0
- package/dist/connection.d.ts.map +1 -0
- package/dist/connection.js +16 -0
- package/dist/connection.js.map +1 -0
- package/dist/define-component-plugin.d.ts +38 -0
- package/dist/define-component-plugin.d.ts.map +1 -0
- package/dist/define-component-plugin.js +120 -0
- package/dist/define-component-plugin.js.map +1 -0
- package/dist/define-plugin.d.ts.map +1 -1
- package/dist/define-plugin.js +13 -6
- package/dist/define-plugin.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +659 -7
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +19 -1
- package/dist/types.js.map +1 -1
- package/package.json +2 -2
package/dist/types.d.ts
CHANGED
|
@@ -23,6 +23,59 @@ export interface NormalizedIssue {
|
|
|
23
23
|
blockedBy: string[];
|
|
24
24
|
updatedAt: string;
|
|
25
25
|
raw: unknown;
|
|
26
|
+
facetValues?: Record<string, string | string[]>;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Self-reported connectivity for a plugin (host-API 1.1.0+). Plugins that omit
|
|
30
|
+
* `getConnectionStatus` are tolerated; the host falls back to `validateConfig`
|
|
31
|
+
* and infers `connected` vs `auth-problem` from the result.
|
|
32
|
+
*/
|
|
33
|
+
export interface ConnectionStatus {
|
|
34
|
+
state: "connected" | "disconnected" | "auth-problem" | "errored";
|
|
35
|
+
detail?: string;
|
|
36
|
+
/** ISO-8601 timestamp; the plugin (or host fallback) sets this at observation. */
|
|
37
|
+
checkedAt: string;
|
|
38
|
+
/**
|
|
39
|
+
* Present on `connected` when the plugin can cheaply resolve the
|
|
40
|
+
* authenticated account (e.g. from the same `GET /user` probe). The host
|
|
41
|
+
* forwards it verbatim to the UI's "Connected as <login>" label; omit it
|
|
42
|
+
* otherwise. Kept in sync with `ConnectionStatus` in `@roubo/shared`.
|
|
43
|
+
*/
|
|
44
|
+
account?: {
|
|
45
|
+
login: string;
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* One descriptor returned by `filterFacets`. Core renders generic filter UI
|
|
50
|
+
* from these; for `enum-async` the host requests options lazily on dropdown
|
|
51
|
+
* open via `getFacetOptions`. Plugins built against host-API 1.0.0 omit
|
|
52
|
+
* `filterFacets` and core falls back to a fixed common-facet set.
|
|
53
|
+
*/
|
|
54
|
+
export interface FilterFacet {
|
|
55
|
+
id: string;
|
|
56
|
+
label: string;
|
|
57
|
+
type: "enum" | "enum-async" | "multi-enum";
|
|
58
|
+
options?: FilterFacetOption[];
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* One option for a `FilterFacet`. Used both inline (eager `enum`/`multi-enum`)
|
|
62
|
+
* and as the return shape of `getFacetOptions` (lazy `enum-async`).
|
|
63
|
+
*/
|
|
64
|
+
export interface FilterFacetOption {
|
|
65
|
+
value: string;
|
|
66
|
+
label: string;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* One sort field returned by `getSortFields` (host-API 1.2.0+, CLI-FR-009).
|
|
70
|
+
* Core renders a sort picker from these; `defaultDir` is the direction first
|
|
71
|
+
* applied when the user selects the field. Plugins built against host-API
|
|
72
|
+
* 1.0.0 / 1.1.0 omit `getSortFields` and core renders no picker (CLI-FR-011).
|
|
73
|
+
* Mirrored as `SortField` in `@roubo/shared`.
|
|
74
|
+
*/
|
|
75
|
+
export interface SortField {
|
|
76
|
+
id: string;
|
|
77
|
+
label: string;
|
|
78
|
+
defaultDir: "asc" | "desc";
|
|
26
79
|
}
|
|
27
80
|
export interface NormalizedComment {
|
|
28
81
|
externalId: string;
|
|
@@ -34,23 +87,194 @@ export interface NormalizedComment {
|
|
|
34
87
|
createdAt: string;
|
|
35
88
|
updatedAt: string;
|
|
36
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* One entry of the source list a host passes into source-bound contract
|
|
92
|
+
* methods. `kind` is plugin-defined (e.g. `"repo"`, `"project"` for the
|
|
93
|
+
* GitHub plugins); `externalId` is the plugin-native id for that source
|
|
94
|
+
* (e.g. `"owner/repo"`, `"owner/#42"`). The host derives this list per
|
|
95
|
+
* request from the project's `roubo.yaml` integration block, so plugins
|
|
96
|
+
* never share source state across projects.
|
|
97
|
+
*/
|
|
98
|
+
export interface ConfiguredSource {
|
|
99
|
+
kind: string;
|
|
100
|
+
externalId: string;
|
|
101
|
+
/**
|
|
102
|
+
* Jira self-hosted only: the project key this source is scoped to under the
|
|
103
|
+
* project-first selection model. Plugins outside the Jira family ignore it.
|
|
104
|
+
*/
|
|
105
|
+
project?: string;
|
|
106
|
+
/**
|
|
107
|
+
* Jira self-hosted only: for a `board` source, whether to resolve to the
|
|
108
|
+
* board's active sprint (default) or the whole board's backing filter.
|
|
109
|
+
*/
|
|
110
|
+
boardMode?: "active-sprint" | "whole-board";
|
|
111
|
+
/**
|
|
112
|
+
* Jira self-hosted only: for the synthetic `mine` ("assigned to me") source,
|
|
113
|
+
* whether it is scoped to the in-scope projects or matches anywhere.
|
|
114
|
+
*/
|
|
115
|
+
mineScope?: "in-project" | "anywhere";
|
|
116
|
+
/**
|
|
117
|
+
* github.com / GHE only: per-source toggles for the GitHub Advanced Security
|
|
118
|
+
* alert categories surfaced as security-* issue types. Plugins outside the
|
|
119
|
+
* GitHub family ignore these fields. Default false on each.
|
|
120
|
+
*/
|
|
121
|
+
includeCodeQLAlerts?: boolean;
|
|
122
|
+
includeSecretScanningAlerts?: boolean;
|
|
123
|
+
includeDependabotAlerts?: boolean;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Discriminator for `ListIssuesWarning.code`. The client maps these to chip
|
|
127
|
+
* variants in the cut-list source picker. `missing-scope` and
|
|
128
|
+
* `scope-unverifiable` drive the GitHub family's PAT/OAuth remediation
|
|
129
|
+
* affordances (WU-032); other codes share the generic "Unavailable" chip.
|
|
130
|
+
*/
|
|
131
|
+
export type ListIssuesWarningCode = "missing-scope" | "scope-unverifiable" | "feature-disabled" | "insufficient-permission" | "not-found" | "rate-limited" | "unknown";
|
|
132
|
+
/**
|
|
133
|
+
* Non-fatal warning emitted alongside a `listIssues` result. Used by the
|
|
134
|
+
* GitHub plugins to surface per-source per-category fetch failures without
|
|
135
|
+
* failing the entire pull. Categories are stable string identifiers; the
|
|
136
|
+
* host treats unknown values as opaque and surfaces `cause` verbatim to UI.
|
|
137
|
+
*
|
|
138
|
+
* `code` is an optional discriminator the client uses to pick a chip variant
|
|
139
|
+
* (e.g. `missing-scope` → link chip pointing at PAT settings / OAuth re-auth).
|
|
140
|
+
* Absent means the client renders the generic chip with `cause` as the tooltip.
|
|
141
|
+
*
|
|
142
|
+
* A warning with a given `(sourceExternalId, category)` is cleared on the
|
|
143
|
+
* next successful pull for that pair: a subsequent `listIssues` page-1
|
|
144
|
+
* result that omits it constitutes a clear.
|
|
145
|
+
*/
|
|
146
|
+
export interface ListIssuesWarning {
|
|
147
|
+
category: "code-scanning" | "secret-scanning" | "dependabot" | string;
|
|
148
|
+
sourceExternalId: string;
|
|
149
|
+
cause: string;
|
|
150
|
+
code?: ListIssuesWarningCode;
|
|
151
|
+
detail?: {
|
|
152
|
+
status?: number;
|
|
153
|
+
code?: string;
|
|
154
|
+
missingScope?: string;
|
|
155
|
+
};
|
|
156
|
+
}
|
|
37
157
|
export interface ListIssuesParams {
|
|
158
|
+
sources: ConfiguredSource[];
|
|
38
159
|
cursor: string | null;
|
|
39
160
|
pageSize: number;
|
|
40
161
|
filters?: {
|
|
41
162
|
labels?: string[];
|
|
42
163
|
search?: string;
|
|
43
164
|
};
|
|
165
|
+
/**
|
|
166
|
+
* Status exclusion resolved by the host from the three-layer merge (FR-009,
|
|
167
|
+
* FR-010), applied in the query so excluded issues never occupy a result
|
|
168
|
+
* page. `excludedStatusCategories` is the category-first default (e.g.
|
|
169
|
+
* `["Done"]`); `excludedStatuses` is the status-name list a plugin uses as
|
|
170
|
+
* the fallback when the instance does not support `statusCategory` in its
|
|
171
|
+
* query language. A plugin that does not do server-side exclusion ignores both.
|
|
172
|
+
*/
|
|
173
|
+
excludedStatusCategories?: string[];
|
|
174
|
+
excludedStatuses?: string[];
|
|
175
|
+
/**
|
|
176
|
+
* Plugin-declared sort selection (CLI-FR-009/CLI-FR-010). `sortBy` is one of
|
|
177
|
+
* the field ids the plugin returned from `getSortFields`; `sortDir` is the
|
|
178
|
+
* direction. Plugins MUST apply the sort source-side so the order is stable
|
|
179
|
+
* across pages. Absent means the plugin's natural order; a plugin that does
|
|
180
|
+
* not declare any sort fields (and so never receives these) is unaffected.
|
|
181
|
+
*/
|
|
182
|
+
sortBy?: string;
|
|
183
|
+
sortDir?: "asc" | "desc";
|
|
184
|
+
}
|
|
185
|
+
export interface ListIssueTypesParams {
|
|
186
|
+
sources: ConfiguredSource[];
|
|
187
|
+
}
|
|
188
|
+
export interface ListLabelsParams {
|
|
189
|
+
sources: ConfiguredSource[];
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Params for the lazy facet-option loader. `facetId` matches a `FilterFacet.id`
|
|
193
|
+
* the plugin previously returned from `filterFacets()`. `sources` follows the
|
|
194
|
+
* existing source-bound pattern so plugins remain stateless across projects.
|
|
195
|
+
* `search` is the optional user-typed prefix/substring; plugins MAY ignore it
|
|
196
|
+
* and return the full set.
|
|
197
|
+
*/
|
|
198
|
+
export interface GetFacetOptionsParams {
|
|
199
|
+
facetId: string;
|
|
200
|
+
sources: ConfiguredSource[];
|
|
201
|
+
search?: string;
|
|
44
202
|
}
|
|
45
203
|
export interface ListIssuesResult {
|
|
46
204
|
items: NormalizedIssue[];
|
|
47
205
|
nextCursor: string | null;
|
|
206
|
+
/** Absent or empty means "no per-category problems on this page." */
|
|
207
|
+
warnings?: ListIssuesWarning[];
|
|
208
|
+
/**
|
|
209
|
+
* Count of issues the plugin dropped in-query (e.g. the status-category
|
|
210
|
+
* exclusion of FR-009/FR-010), surfaced so the cut list can show "N filtered
|
|
211
|
+
* out by status". Additive and optional: the host sums it across pages and
|
|
212
|
+
* treats absence as "unknown". Plugins that filter in memory report it
|
|
213
|
+
* per page; the jira-self-hosted plugin excludes server-side in JQL, so it
|
|
214
|
+
* reports the whole-result-set count once on the first page via a count-only
|
|
215
|
+
* companion query (and omits it when the companion count is unavailable).
|
|
216
|
+
*/
|
|
217
|
+
excludedCount?: number;
|
|
48
218
|
}
|
|
49
|
-
export
|
|
50
|
-
|
|
219
|
+
export type SourceCandidateIcon = "repo" | "project" | "board" | "epic" | "filter";
|
|
220
|
+
export interface SourceCandidateItem {
|
|
51
221
|
externalId: string;
|
|
52
|
-
|
|
53
|
-
|
|
222
|
+
label: string;
|
|
223
|
+
sublabel?: string;
|
|
224
|
+
icon?: SourceCandidateIcon;
|
|
225
|
+
}
|
|
226
|
+
export interface SourceCandidateCategory {
|
|
227
|
+
id: string;
|
|
228
|
+
label: string;
|
|
229
|
+
items: SourceCandidateItem[];
|
|
230
|
+
}
|
|
231
|
+
export type SourceCandidatesShape = "multi-list" | "categorized-multi-list" | "searchable-categorized";
|
|
232
|
+
export interface SourceCategoryOption {
|
|
233
|
+
id: string;
|
|
234
|
+
label: string;
|
|
235
|
+
}
|
|
236
|
+
export interface SearchableSourceCategory {
|
|
237
|
+
id: "project" | "board" | "filter" | "epic" | "mine";
|
|
238
|
+
label: string;
|
|
239
|
+
icon?: SourceCandidateIcon;
|
|
240
|
+
scopedBy?: "project";
|
|
241
|
+
options?: SourceCategoryOption[];
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Declarative source-picker payload returned by `listSourceCandidates`. Roubo's
|
|
245
|
+
* host renders the UI from this envelope; plugins ship no React. See
|
|
246
|
+
* `.specifications/integration-plugins/architecture.md`.
|
|
247
|
+
*/
|
|
248
|
+
export interface SourceCandidatesResponse {
|
|
249
|
+
shape: SourceCandidatesShape;
|
|
250
|
+
items?: SourceCandidateItem[];
|
|
251
|
+
categories?: SourceCandidateCategory[];
|
|
252
|
+
searchableCategories?: SearchableSourceCategory[];
|
|
253
|
+
nextCursor?: string | null;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Params for the scoped, paginated source-option search (`getSourceOptions`).
|
|
257
|
+
* Generalizes `getFacetOptions` with a parent `scope` (e.g. the Jira project
|
|
258
|
+
* keys a board/filter/epic search is confined to) and an opaque `cursor`.
|
|
259
|
+
* `search` is the optional user-typed term (debounced client-side); plugins
|
|
260
|
+
* MAY ignore it. Scoped categories with no `scope.project` return an empty page.
|
|
261
|
+
*/
|
|
262
|
+
export interface GetSourceOptionsParams {
|
|
263
|
+
category: "project" | "board" | "filter" | "epic";
|
|
264
|
+
scope?: {
|
|
265
|
+
project?: string[];
|
|
266
|
+
};
|
|
267
|
+
search?: string;
|
|
268
|
+
cursor?: string | null;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* One page of source options. `nextCursor` is an opaque token the host passes
|
|
272
|
+
* back verbatim to fetch the following page; `null` means the result set is
|
|
273
|
+
* exhausted (NFR-004: every item reachable, no page dropped or duplicated).
|
|
274
|
+
*/
|
|
275
|
+
export interface SourceOptionsResult {
|
|
276
|
+
items: SourceCandidateItem[];
|
|
277
|
+
nextCursor: string | null;
|
|
54
278
|
}
|
|
55
279
|
export interface CurrentUser {
|
|
56
280
|
externalId: string;
|
|
@@ -64,16 +288,102 @@ export interface ValidateConfigResult {
|
|
|
64
288
|
code?: string;
|
|
65
289
|
}>;
|
|
66
290
|
}
|
|
291
|
+
/**
|
|
292
|
+
* Result of a lightweight activation call (`setActiveConfig`). Plugins that
|
|
293
|
+
* hold plugin-wide configuration (e.g. an API instance URL, TLS toggles)
|
|
294
|
+
* implement this to receive that configuration before source-bound RPCs run.
|
|
295
|
+
*
|
|
296
|
+
* `setActiveConfig` is no longer used to convey per-project state: source
|
|
297
|
+
* selections flow through `sources` on each source-bound call so the plugin
|
|
298
|
+
* process holds no per-project state. Plugins with no plugin-wide config
|
|
299
|
+
* (e.g. github.com, which has a fixed API host) can skip implementing this
|
|
300
|
+
* method entirely.
|
|
301
|
+
*/
|
|
302
|
+
export interface SetActiveConfigResult {
|
|
303
|
+
ok: boolean;
|
|
304
|
+
errors?: Array<{
|
|
305
|
+
field?: string;
|
|
306
|
+
message: string;
|
|
307
|
+
code?: string;
|
|
308
|
+
}>;
|
|
309
|
+
}
|
|
67
310
|
export interface IssueTypeOption {
|
|
68
311
|
id: string;
|
|
69
312
|
name: string;
|
|
70
313
|
}
|
|
314
|
+
/**
|
|
315
|
+
* Result of the privileged `createIssue` op (verify-gate FR-011, spike #704).
|
|
316
|
+
* `ref` is the created issue's external id in the plugin's own form
|
|
317
|
+
* (`owner/repo#number` for GitHub) so the host can immediately use it (e.g. as
|
|
318
|
+
* `blockerRef` in `addBlockedBy`). `nodeId` is the provider's GraphQL node id
|
|
319
|
+
* when the tracker exposes one, omitted otherwise.
|
|
320
|
+
*/
|
|
321
|
+
export interface CreateIssueResult {
|
|
322
|
+
ref: string;
|
|
323
|
+
url: string;
|
|
324
|
+
nodeId?: string;
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Stable identifier for an alert category probed by `probeAlertCategories`.
|
|
328
|
+
* The host's Test Connection result strip surfaces one row per probe result.
|
|
329
|
+
*/
|
|
330
|
+
export type ProbeAlertCategory = "code-scanning" | "secret-scanning" | "dependabot";
|
|
331
|
+
/**
|
|
332
|
+
* Per-probe status returned by `probeAlertCategories`. The host maps these
|
|
333
|
+
* directly into result-strip rows; semantics match the host's
|
|
334
|
+
* `IntegrationCategoryStatus`:
|
|
335
|
+
*
|
|
336
|
+
* - `ok`: probe succeeded (HTTP 2xx)
|
|
337
|
+
* - `scope-missing`: token lacks the required scope (HTTP 401/403)
|
|
338
|
+
* - `not-enabled`: feature is not enabled for the probed repo (HTTP 404/410/451)
|
|
339
|
+
* - `timed-out`: probe exceeded the per-probe cap. Rendered as an amber
|
|
340
|
+
* warning; does not fail the overall Test Connection result.
|
|
341
|
+
* - `error`: probe returned an unexpected status or threw a non-timeout error
|
|
342
|
+
*/
|
|
343
|
+
export type ProbeAlertCategoryStatus = "ok" | "scope-missing" | "not-enabled" | "timed-out" | "error";
|
|
344
|
+
export interface ProbeAlertCategoriesParams {
|
|
345
|
+
/**
|
|
346
|
+
* The same source list a host would pass to `listIssues`. The plugin picks
|
|
347
|
+
* its sample target from this list (typically the first repo source) and
|
|
348
|
+
* may return an `error` row for every requested category if none of the
|
|
349
|
+
* sources are probeable.
|
|
350
|
+
*/
|
|
351
|
+
sources: ConfiguredSource[];
|
|
352
|
+
/** Subset of categories the host wants probed; never empty. */
|
|
353
|
+
enabledCategories: ProbeAlertCategory[];
|
|
354
|
+
/**
|
|
355
|
+
* Host-supplied hint for the per-probe timeout. Plugins SHOULD honour this;
|
|
356
|
+
* the host defaults to 5000ms when omitted (FR-047: 5s per-probe cap).
|
|
357
|
+
*/
|
|
358
|
+
timeoutMsPerProbe?: number;
|
|
359
|
+
}
|
|
360
|
+
export interface ProbeAlertCategoryReport {
|
|
361
|
+
category: ProbeAlertCategory;
|
|
362
|
+
status: ProbeAlertCategoryStatus;
|
|
363
|
+
detail?: string;
|
|
364
|
+
httpStatus?: number;
|
|
365
|
+
}
|
|
366
|
+
export interface ProbeAlertCategoriesResult {
|
|
367
|
+
reports: ProbeAlertCategoryReport[];
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Result of directly probing access to a single source (e.g. a GitHub repo).
|
|
371
|
+
* Lets the host distinguish "no such source" from "access blocked by policy"
|
|
372
|
+
* when a source is missing from `listSourceCandidates`: `status` and `message`
|
|
373
|
+
* carry the underlying HTTP error verbatim so the host can classify it into an
|
|
374
|
+
* actionable code rather than a generic miss.
|
|
375
|
+
*/
|
|
376
|
+
export interface ProbeRepoAccessResult {
|
|
377
|
+
accessible: boolean;
|
|
378
|
+
status?: number;
|
|
379
|
+
message?: string;
|
|
380
|
+
}
|
|
71
381
|
/**
|
|
72
382
|
* The contract methods a plugin may implement. All methods are optional;
|
|
73
383
|
* a host call to an unimplemented method receives JSON-RPC MethodNotFound.
|
|
74
384
|
*/
|
|
75
385
|
export interface PluginContract {
|
|
76
|
-
listSourceCandidates?: () => Promise<
|
|
386
|
+
listSourceCandidates?: () => Promise<SourceCandidatesResponse> | SourceCandidatesResponse;
|
|
77
387
|
listIssues?: (params: ListIssuesParams) => Promise<ListIssuesResult> | ListIssuesResult;
|
|
78
388
|
getIssue?: (params: {
|
|
79
389
|
externalId: string;
|
|
@@ -85,10 +395,40 @@ export interface PluginContract {
|
|
|
85
395
|
validateConfig?: (params: {
|
|
86
396
|
config: Record<string, unknown>;
|
|
87
397
|
}) => Promise<ValidateConfigResult> | ValidateConfigResult;
|
|
398
|
+
setActiveConfig?: (params: {
|
|
399
|
+
config: Record<string, unknown>;
|
|
400
|
+
}) => Promise<SetActiveConfigResult> | SetActiveConfigResult;
|
|
88
401
|
applyTransition?: (params: {
|
|
89
402
|
externalId: string;
|
|
90
403
|
transition: string;
|
|
91
404
|
}) => Promise<void> | void;
|
|
405
|
+
/**
|
|
406
|
+
* Create a tracker issue (verify-gate FR-011, spike #704). Privileged write
|
|
407
|
+
* routed only through the host's TrackerActionGateway, which gates it on the
|
|
408
|
+
* `supportsCreateIssue` manifest capability and the plugin's consent. Returns
|
|
409
|
+
* the created issue's external ref (the same `owner/repo#number` form the
|
|
410
|
+
* other issue-scoped methods accept), its URL, and the provider node id when
|
|
411
|
+
* the tracker exposes one (GitHub's GraphQL node id, used to wire blocking
|
|
412
|
+
* links without a second lookup).
|
|
413
|
+
*/
|
|
414
|
+
createIssue?: (params: {
|
|
415
|
+
repoFullName: string;
|
|
416
|
+
title: string;
|
|
417
|
+
body?: string;
|
|
418
|
+
labels?: string[];
|
|
419
|
+
}) => Promise<CreateIssueResult> | CreateIssueResult;
|
|
420
|
+
/**
|
|
421
|
+
* Register an "is blocked by" relationship: `blockedRef` is blocked by
|
|
422
|
+
* `blockerRef` (verify-gate FR-010/FR-011, spike #704). Privileged write
|
|
423
|
+
* routed only through the host's TrackerActionGateway, which gates it on the
|
|
424
|
+
* `supportsBlockingLinks` manifest capability and the plugin's consent. Both
|
|
425
|
+
* refs are external ids in the plugin's own form (`owner/repo#number` for
|
|
426
|
+
* GitHub).
|
|
427
|
+
*/
|
|
428
|
+
addBlockedBy?: (params: {
|
|
429
|
+
blockedRef: string;
|
|
430
|
+
blockerRef: string;
|
|
431
|
+
}) => Promise<void> | void;
|
|
92
432
|
assignIssue?: (params: {
|
|
93
433
|
externalId: string;
|
|
94
434
|
assigneeExternalId: string;
|
|
@@ -100,8 +440,52 @@ export interface PluginContract {
|
|
|
100
440
|
getAvailableTransitions?: (params: {
|
|
101
441
|
externalId: string;
|
|
102
442
|
}) => Promise<string[]> | string[];
|
|
103
|
-
listIssueTypes?: () => Promise<IssueTypeOption[]> | IssueTypeOption[];
|
|
104
|
-
|
|
443
|
+
listIssueTypes?: (params: ListIssueTypesParams) => Promise<IssueTypeOption[]> | IssueTypeOption[];
|
|
444
|
+
/**
|
|
445
|
+
* Enumerate the connected instance's available status categories (issue #453).
|
|
446
|
+
* The host exposes these as the option list for the Configure dialog's
|
|
447
|
+
* status-category exclusion toggle, falling back to a canonical set when a
|
|
448
|
+
* plugin does not implement this method (`MethodNotFound`) or discovery fails.
|
|
449
|
+
* Returned names must be valid wherever the plugin consumes excluded
|
|
450
|
+
* categories (e.g. Jira returns `statusCategory` names usable in JQL).
|
|
451
|
+
*/
|
|
452
|
+
listStatusCategories?: () => Promise<string[]> | string[];
|
|
453
|
+
listLabels?: (params: ListLabelsParams) => Promise<string[]> | string[];
|
|
454
|
+
getConnectionStatus?: () => Promise<ConnectionStatus> | ConnectionStatus;
|
|
455
|
+
/**
|
|
456
|
+
* Probe each requested alert-category endpoint for a sample source and
|
|
457
|
+
* return one report per category. Invoked by the host as part of Test
|
|
458
|
+
* Connection (FR-047, WU-041). A throw or `MethodNotFound` is treated by
|
|
459
|
+
* the host as "no per-category data"; it never fails the overall test.
|
|
460
|
+
*/
|
|
461
|
+
probeAlertCategories?: (params: ProbeAlertCategoriesParams) => Promise<ProbeAlertCategoriesResult> | ProbeAlertCategoriesResult;
|
|
462
|
+
/**
|
|
463
|
+
* Directly probe access to a single repo (`GET /repos/{owner}/{repo}`) so the
|
|
464
|
+
* host can explain why a configured repo is missing from
|
|
465
|
+
* `listSourceCandidates` (e.g. org OAuth App access restrictions), rather than
|
|
466
|
+
* silently reporting "not found".
|
|
467
|
+
*/
|
|
468
|
+
probeRepoAccess?: (params: {
|
|
469
|
+
repoFullName: string;
|
|
470
|
+
}) => Promise<ProbeRepoAccessResult> | ProbeRepoAccessResult;
|
|
471
|
+
filterFacets?: () => Promise<FilterFacet[]> | FilterFacet[];
|
|
472
|
+
getFacetOptions?: (params: GetFacetOptionsParams) => Promise<FilterFacetOption[]> | FilterFacetOption[];
|
|
473
|
+
/**
|
|
474
|
+
* Declare the sort fields the cut-list picker offers (host-API 1.2.0+,
|
|
475
|
+
* CLI-FR-009). Each field carries a stable `id` (forwarded back as
|
|
476
|
+
* `ListIssuesParams.sortBy`), a human `label`, and a `defaultDir`. Plugins
|
|
477
|
+
* omitting this method resolve to `MethodNotFound`, which core maps to an
|
|
478
|
+
* empty list so no picker renders (CLI-FR-011). A plugin that declares fields
|
|
479
|
+
* MUST honour `sortBy`/`sortDir` source-side in `listIssues` (CLI-FR-010).
|
|
480
|
+
*/
|
|
481
|
+
getSortFields?: () => Promise<SortField[]> | SortField[];
|
|
482
|
+
/**
|
|
483
|
+
* Scoped, paginated, type-ahead search over a plugin's selectable source
|
|
484
|
+
* categories (project / board / filter / epic). The host calls this from the
|
|
485
|
+
* searchable source picker as the user types and pages; the plugin stays
|
|
486
|
+
* stateless across calls (the parent `scope` is supplied each time).
|
|
487
|
+
*/
|
|
488
|
+
getSourceOptions?: (params: GetSourceOptionsParams) => Promise<SourceOptionsResult> | SourceOptionsResult;
|
|
105
489
|
}
|
|
106
490
|
export type ContractMethodName = keyof PluginContract;
|
|
107
491
|
export interface FetchInit {
|
|
@@ -153,4 +537,272 @@ export interface PluginHandle {
|
|
|
153
537
|
/** Tear down the RPC connection. Tests use this; production plugins do not. */
|
|
154
538
|
dispose(): void;
|
|
155
539
|
}
|
|
540
|
+
/**
|
|
541
|
+
* The SDK-level contract version a component plugin declares. The host gates
|
|
542
|
+
* compatibility at validation time (a mismatch is rejected before any
|
|
543
|
+
* lifecycle method is called, never at call time). A single integer mirrors
|
|
544
|
+
* the `schemaVersion: 1` precedent on `ProvisionDescriptor`.
|
|
545
|
+
*/
|
|
546
|
+
export declare const SUPPORTED_CONTRACT_VERSION: 1;
|
|
547
|
+
/**
|
|
548
|
+
* Minimal structural copy of `@roubo/shared`'s `ProvisionDescriptor` union.
|
|
549
|
+
*
|
|
550
|
+
* `@roubo/plugin-sdk` is a published, dependency-light package (`private:
|
|
551
|
+
* false`, only `vscode-jsonrpc`) whereas `@roubo/shared` is a `private: true`
|
|
552
|
+
* workspace package that ships raw TypeScript. Taking a workspace dependency on
|
|
553
|
+
* it would break both `npm publish` (an unpublished dependency) and the SDK's
|
|
554
|
+
* own `tsc` build (`rootDir: ./src` cannot import a `.ts` file outside `src`).
|
|
555
|
+
* So the descriptor shape is restated here. It MUST stay structurally in sync
|
|
556
|
+
* with `shared/provision-descriptor-schema.ts` (the Zod schema is the
|
|
557
|
+
* authority; the host validates every descriptor against it).
|
|
558
|
+
*/
|
|
559
|
+
export interface DockerProvisionDescriptor {
|
|
560
|
+
schemaVersion: 1;
|
|
561
|
+
kind: "docker";
|
|
562
|
+
composeFile: string;
|
|
563
|
+
service: string;
|
|
564
|
+
initService?: string;
|
|
565
|
+
portEnvVar?: string;
|
|
566
|
+
migration?: {
|
|
567
|
+
command: string;
|
|
568
|
+
args?: string[];
|
|
569
|
+
};
|
|
570
|
+
connection?: {
|
|
571
|
+
template: string;
|
|
572
|
+
};
|
|
573
|
+
assignedContainerId?: string;
|
|
574
|
+
env?: Record<string, string>;
|
|
575
|
+
healthcheck?: boolean;
|
|
576
|
+
}
|
|
577
|
+
export interface ProcessProvisionDescriptor {
|
|
578
|
+
schemaVersion: 1;
|
|
579
|
+
kind: "process";
|
|
580
|
+
command: string;
|
|
581
|
+
env?: Record<string, string>;
|
|
582
|
+
envFile?: string;
|
|
583
|
+
cwd?: string;
|
|
584
|
+
setup?: string;
|
|
585
|
+
dependsOn?: string[];
|
|
586
|
+
}
|
|
587
|
+
export interface OneshotProvisionDescriptor {
|
|
588
|
+
schemaVersion: 1;
|
|
589
|
+
kind: "oneshot";
|
|
590
|
+
command: string;
|
|
591
|
+
env?: Record<string, string>;
|
|
592
|
+
envFile?: string;
|
|
593
|
+
cwd?: string;
|
|
594
|
+
dependsOn?: string[];
|
|
595
|
+
timeoutMs?: number;
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* Discriminated (on `kind`) union a declarative component plugin returns from
|
|
599
|
+
* `translate`. The host's `LifecycleEngine` validates it against the supported
|
|
600
|
+
* `schemaVersion` and then executes it.
|
|
601
|
+
*/
|
|
602
|
+
export type ProvisionDescriptor = DockerProvisionDescriptor | ProcessProvisionDescriptor | OneshotProvisionDescriptor;
|
|
603
|
+
/**
|
|
604
|
+
* Per-bench context the host resolves (ports allocated, env merged) before any
|
|
605
|
+
* lifecycle method runs. A component plugin is spawned once per plugin and
|
|
606
|
+
* multiplexes benches; `benchId` distinguishes the active bench so a single
|
|
607
|
+
* process serves several concurrently. Mirrors `BenchContext` in
|
|
608
|
+
* architecture.md ('Data model').
|
|
609
|
+
*/
|
|
610
|
+
export interface BenchContext {
|
|
611
|
+
projectId: string;
|
|
612
|
+
benchId: number;
|
|
613
|
+
componentName: string;
|
|
614
|
+
workspacePath: string;
|
|
615
|
+
ports: Record<string, number>;
|
|
616
|
+
env: Record<string, string>;
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Lifecycle status a component plugin reports (imperative `health`, or pushed
|
|
620
|
+
* via `host.component.reportStatus`). `completed` is the terminal state for a
|
|
621
|
+
* successful one-shot lifecycle (FR-014 / FR-022 delta), distinct from
|
|
622
|
+
* `stopped` (idle) and `error`. This SDK-facing shape intentionally diverges
|
|
623
|
+
* from `@roubo/shared`'s `ComponentStatus`: it adds the `completed` status,
|
|
624
|
+
* treats `name` / `setupComplete` as optional (or absent) rather than required,
|
|
625
|
+
* and models `phases` as a `Record<string, string>` rather than a
|
|
626
|
+
* `ComponentPhase[]`. The shared type stays authoritative host-side; keep the
|
|
627
|
+
* two reconciled deliberately, not field-for-field identical.
|
|
628
|
+
*/
|
|
629
|
+
export interface ComponentStatus {
|
|
630
|
+
status: "stopped" | "starting" | "running" | "error" | "stopping" | "completed";
|
|
631
|
+
pid?: number;
|
|
632
|
+
containerId?: string;
|
|
633
|
+
phases?: Record<string, string>;
|
|
634
|
+
setupComplete?: boolean;
|
|
635
|
+
error?: string;
|
|
636
|
+
statusDetail?: string;
|
|
637
|
+
startedAt?: string;
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* Declarative (preferred) component contract: a pure function mapping the
|
|
641
|
+
* plugin's `config` plus the `BenchContext` to a `ProvisionDescriptor` the host
|
|
642
|
+
* executes. A plugin implements EITHER `translate` OR the imperative hooks
|
|
643
|
+
* below, never both (`defineComponentPlugin` rejects both at validation time).
|
|
644
|
+
*/
|
|
645
|
+
export interface DeclarativeComponentContract {
|
|
646
|
+
translate: (params: {
|
|
647
|
+
config: Record<string, unknown>;
|
|
648
|
+
context: BenchContext;
|
|
649
|
+
}) => Promise<ProvisionDescriptor> | ProvisionDescriptor;
|
|
650
|
+
start?: never;
|
|
651
|
+
stop?: never;
|
|
652
|
+
health?: never;
|
|
653
|
+
cleanup?: never;
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
656
|
+
* Imperative (escape-hatch) component contract: lifecycle hooks the plugin
|
|
657
|
+
* drives through the broker (`host.process.*`, `host.docker.*`, `host.ports.*`)
|
|
658
|
+
* for a novel lifecycle a `ProvisionDescriptor` cannot express. All four hooks
|
|
659
|
+
* are required so the host never reaches a half-implemented lifecycle (a plugin
|
|
660
|
+
* missing `stop` is rejected at validation, not at stop-time).
|
|
661
|
+
*/
|
|
662
|
+
export interface ImperativeComponentContract {
|
|
663
|
+
start: (context: BenchContext) => Promise<void> | void;
|
|
664
|
+
stop: (context: BenchContext) => Promise<void> | void;
|
|
665
|
+
health: (context: BenchContext) => Promise<ComponentStatus> | ComponentStatus;
|
|
666
|
+
cleanup: (context: BenchContext) => Promise<void> | void;
|
|
667
|
+
translate?: never;
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* The contract a component plugin implements: the declarative `translate` path
|
|
671
|
+
* XOR the imperative lifecycle hooks. The TypeScript `never` guards make the
|
|
672
|
+
* two variants mutually exclusive at compile time; `defineComponentPlugin` also
|
|
673
|
+
* enforces the rule at runtime/validation time.
|
|
674
|
+
*/
|
|
675
|
+
export type ComponentContract = DeclarativeComponentContract | ImperativeComponentContract;
|
|
676
|
+
export type ComponentContractMethodName = "translate" | "start" | "stop" | "health" | "cleanup";
|
|
677
|
+
/** Options for `defineComponentPlugin`. */
|
|
678
|
+
export interface DefineComponentPluginOptions {
|
|
679
|
+
/**
|
|
680
|
+
* The contract version the plugin declares. Must equal
|
|
681
|
+
* `SUPPORTED_CONTRACT_VERSION`; a mismatch is rejected synchronously at
|
|
682
|
+
* definition (validation) time, never deferred to a lifecycle call.
|
|
683
|
+
* Defaults to `SUPPORTED_CONTRACT_VERSION` when omitted.
|
|
684
|
+
*/
|
|
685
|
+
contractVersion?: number;
|
|
686
|
+
/**
|
|
687
|
+
* Replace the default stdio streams. Test harnesses inject paired streams;
|
|
688
|
+
* production plugin code never sets this.
|
|
689
|
+
*/
|
|
690
|
+
streams?: {
|
|
691
|
+
input: NodeJS.ReadableStream;
|
|
692
|
+
output: NodeJS.WritableStream;
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
/** Result of `host.process.run` (a blocking run-to-completion). */
|
|
696
|
+
export interface ProcessRunResult {
|
|
697
|
+
exitCode: number;
|
|
698
|
+
}
|
|
699
|
+
/** Result of `host.process.status`. */
|
|
700
|
+
export interface ProcessStatusResult {
|
|
701
|
+
alive: boolean;
|
|
702
|
+
exitCode?: number;
|
|
703
|
+
}
|
|
704
|
+
/** Result of `host.capability.query` (the FR-017 graceful version gate). */
|
|
705
|
+
export interface CapabilityQueryResult {
|
|
706
|
+
available: boolean;
|
|
707
|
+
introducedIn?: string;
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* The host client surface available to a component plugin inside its contract
|
|
711
|
+
* methods. Each call is an RPC request (or notification) over the bound
|
|
712
|
+
* connection. The host owns every process and container handle; the plugin
|
|
713
|
+
* never spawns anything itself. Mirrors the broker surface in architecture.md.
|
|
714
|
+
*/
|
|
715
|
+
export interface ComponentHostClient {
|
|
716
|
+
process: {
|
|
717
|
+
start(params: {
|
|
718
|
+
id: string;
|
|
719
|
+
command: string;
|
|
720
|
+
args?: string[];
|
|
721
|
+
env: Record<string, string>;
|
|
722
|
+
cwd: string;
|
|
723
|
+
}): Promise<{
|
|
724
|
+
pid: number;
|
|
725
|
+
}>;
|
|
726
|
+
run(params: {
|
|
727
|
+
id: string;
|
|
728
|
+
command: string;
|
|
729
|
+
args?: string[];
|
|
730
|
+
env: Record<string, string>;
|
|
731
|
+
cwd: string;
|
|
732
|
+
timeoutMs: number;
|
|
733
|
+
}): Promise<ProcessRunResult>;
|
|
734
|
+
stop(params: {
|
|
735
|
+
id: string;
|
|
736
|
+
}): Promise<void>;
|
|
737
|
+
status(params: {
|
|
738
|
+
id: string;
|
|
739
|
+
}): Promise<ProcessStatusResult>;
|
|
740
|
+
logs(params: {
|
|
741
|
+
id: string;
|
|
742
|
+
}): Promise<string[]>;
|
|
743
|
+
};
|
|
744
|
+
docker: {
|
|
745
|
+
composeUp(params: {
|
|
746
|
+
projectName: string;
|
|
747
|
+
composeFile: string;
|
|
748
|
+
cwd: string;
|
|
749
|
+
service: string;
|
|
750
|
+
env: Record<string, string>;
|
|
751
|
+
}): Promise<{
|
|
752
|
+
containerId: string;
|
|
753
|
+
}>;
|
|
754
|
+
waitForHealthy(params: {
|
|
755
|
+
projectName: string;
|
|
756
|
+
service: string;
|
|
757
|
+
timeoutMs: number;
|
|
758
|
+
}): Promise<{
|
|
759
|
+
healthy: boolean;
|
|
760
|
+
}>;
|
|
761
|
+
composeRunInit(params: {
|
|
762
|
+
projectName: string;
|
|
763
|
+
composeFile: string;
|
|
764
|
+
cwd: string;
|
|
765
|
+
initService: string;
|
|
766
|
+
}): Promise<void>;
|
|
767
|
+
composeStop(params: {
|
|
768
|
+
projectName: string;
|
|
769
|
+
composeFile: string;
|
|
770
|
+
cwd: string;
|
|
771
|
+
service?: string;
|
|
772
|
+
}): Promise<void>;
|
|
773
|
+
composeDown(params: {
|
|
774
|
+
projectName: string;
|
|
775
|
+
composeFile: string;
|
|
776
|
+
cwd: string;
|
|
777
|
+
}): Promise<void>;
|
|
778
|
+
assignContainer(params: {
|
|
779
|
+
componentName: string;
|
|
780
|
+
containerId: string;
|
|
781
|
+
}): Promise<void>;
|
|
782
|
+
};
|
|
783
|
+
ports: {
|
|
784
|
+
get(params: {
|
|
785
|
+
componentName: string;
|
|
786
|
+
}): Promise<number>;
|
|
787
|
+
};
|
|
788
|
+
component: {
|
|
789
|
+
reportStatus(status: ComponentStatus): void;
|
|
790
|
+
reportLog(params: {
|
|
791
|
+
source: "stdout" | "stderr";
|
|
792
|
+
text: string;
|
|
793
|
+
ts: number;
|
|
794
|
+
}): void;
|
|
795
|
+
};
|
|
796
|
+
capability: {
|
|
797
|
+
query(params: {
|
|
798
|
+
method: string;
|
|
799
|
+
}): Promise<CapabilityQueryResult>;
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
export interface ComponentPluginHandle {
|
|
803
|
+
/** The connected component host client. Available before any hook is called. */
|
|
804
|
+
host: ComponentHostClient;
|
|
805
|
+
/** Tear down the RPC connection. Tests use this; production plugins do not. */
|
|
806
|
+
dispose(): void;
|
|
807
|
+
}
|
|
156
808
|
//# sourceMappingURL=types.d.ts.map
|