@rackbops/ac-plugin-contract 2.0.0-alpha.59 → 2.0.0-alpha.62
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/AUTHORING.md +25 -4
- package/dist/attention.d.ts +3 -2
- package/dist/host-api.d.ts +37 -3
- package/dist/manifest.d.ts +16 -0
- package/dist/views.d.ts +4 -0
- package/package.json +1 -1
- package/schema.json +10 -0
package/AUTHORING.md
CHANGED
|
@@ -198,7 +198,12 @@ is never left open). Register last whatever must not exist when a later step fai
|
|
|
198
198
|
|
|
199
199
|
- **Route** — `host.routes.register(method, path, handler)`. The handler is a web-standard
|
|
200
200
|
`(request: Request) => Response | Promise<Response>`, mounted at `/api/x/<id><path>`. You may only register a
|
|
201
|
-
`(method, path)` you declared in `contributes.routes` — an undeclared route throws.
|
|
201
|
+
`(method, path)` you declared in `contributes.routes` — an undeclared route throws.
|
|
202
|
+
`operator: true` on the route (#867): With the admin kill switch off (`admin_enabled = false`), the host
|
|
203
|
+
refuses this route for its declared method with `403 admin disabled`, as it refuses the admin plugin's own
|
|
204
|
+
surface. Mark an action or route an operator would expect the switch to shut: one that runs a job the job-run
|
|
205
|
+
route refuses, writes to GitHub or the store on the operator's behalf, or changes the console's configuration.
|
|
206
|
+
Additive; a host released before this field refuses a manifest that declares it. **Never answer `502` or
|
|
202
207
|
`504` yourself** (#474): the production console is reached through Cloudflare, and Cloudflare's own
|
|
203
208
|
documentation says it "returns a Cloudflare-branded HTTP 502 or 504 error when your origin web server responds
|
|
204
209
|
with a standard HTTP 502 bad gateway or 504 gateway timeout error" — so on the production console (default
|
|
@@ -237,6 +242,11 @@ is never left open). Register last whatever must not exist when a later step fai
|
|
|
237
242
|
same action again while that locked modal is up reuses it and does not re-run it. After a failed result the
|
|
238
243
|
primary is enabled again and a retry runs, so a double-click whose first run fails fast can run the action
|
|
239
244
|
twice (the second a retry): an action whose failure may have partly applied should tolerate that.
|
|
245
|
+
`operator: true` (#867): With the admin kill switch off (`admin_enabled = false`), the host refuses this
|
|
246
|
+
action (`POST /api/actions/<plugin>/<id>`) with `403 admin disabled`, as it refuses the admin plugin's own
|
|
247
|
+
surface. Mark an action or route an operator would expect the switch to shut: one that runs a job the job-run
|
|
248
|
+
route refuses, writes to GitHub or the store on the operator's behalf, or changes the console's configuration.
|
|
249
|
+
Additive; a host released before this field refuses a manifest that declares it.
|
|
240
250
|
`confirm` stays a valid manifest flag, so an existing manifest keeps loading, but neither the modal nor the
|
|
241
251
|
palette acts on it.
|
|
242
252
|
- **Job** — implement `runJob(ctx)` and dispatch on `ctx.jobId`. The host runs only jobs you declared in
|
|
@@ -1053,6 +1063,14 @@ the machine asleep or off the network, a reboot, a lost NAT mapping) settles `fa
|
|
|
1053
1063
|
after the last byte on the console's Linux host; a machine asleep for longer than that mid-job reads
|
|
1054
1064
|
`failed` even if the job finishes after it wakes.
|
|
1055
1065
|
|
|
1066
|
+
A host restart mid-job is the one end the relay cannot report from inside the new process (#911): the
|
|
1067
|
+
host records each relay it opens and forgets it when the relay sees its end, and
|
|
1068
|
+
`host.agent.unfinishedStreams?()` (optional, like `finished`) returns, once, the calling plugin's relays
|
|
1069
|
+
that an earlier host process opened and never saw end, each `{ taskId, jobId, machineId, machineName,
|
|
1070
|
+
label, openedAt }` (the `taskId` is the earlier process's and no longer exists). Call it once in
|
|
1071
|
+
`activate()` and report each run as ended with an unknown outcome; `plugins/claude-runner` emits
|
|
1072
|
+
`claude.run` `status: "unknown"` for each. `ac jobs run` keeps no record and returns none.
|
|
1073
|
+
|
|
1056
1074
|
```ts
|
|
1057
1075
|
const { jobId } = await execResult // however the plugin's own verb call started the job
|
|
1058
1076
|
const { taskId, finished } = await host.agent.streamJob(machineId, jobId, { label: `code: ${prompt}` })
|
|
@@ -1089,7 +1107,7 @@ interface AttentionItem {
|
|
|
1089
1107
|
href?: string // an external link; wins over `target` when both are set. Linked only
|
|
1090
1108
|
// when its scheme is http:, https:, mailto: or tel: (#722); any other
|
|
1091
1109
|
// href is not linked and `target` is used instead
|
|
1092
|
-
target?: string // an in-app destination: "widget:<cardId>" or "panel:<pluginId>"
|
|
1110
|
+
target?: string // an in-app destination: "widget:<cardId>" (a home card: scrolls to and flashes it; text when the card is not on the page) or "panel:<pluginId>"
|
|
1093
1111
|
hint?: string // the exact command to run, or the reason -- shown as a tooltip/expander
|
|
1094
1112
|
action?: { kind: "issue"; repo?: string; title: string; body: string } // opens provider-github's issue.create, seeded
|
|
1095
1113
|
}
|
|
@@ -1225,9 +1243,9 @@ what it's for.
|
|
|
1225
1243
|
|---|---|---|
|
|
1226
1244
|
| Panel | `panels[]` (`id`, `title`, `route`, `export`, `nav?`, `order?`, `isolation?`) | a component export in `ui.js` |
|
|
1227
1245
|
| Card | `cards[]` (`id`, `title`, `sizes`, `export`) | a component export in `ui.js` |
|
|
1228
|
-
| Action | `actions[]` (`id`, `scope`, `title?`, `confirm?`, `danger?`, `fields?`) | `host.actions.register({ id, run })` |
|
|
1246
|
+
| Action | `actions[]` (`id`, `scope`, `title?`, `confirm?`, `danger?`, `operator?`, `fields?`) | `host.actions.register({ id, run })` |
|
|
1229
1247
|
| Job | `jobs[]` (`id`, `schedule`, `timeoutSec`, `runOn`, `minAgentVersion?`, `capabilities?`) | `runJob(ctx)` dispatching on `ctx.jobId` |
|
|
1230
|
-
| Route | `routes[]` (`method`, `path`) | `host.routes.register(method, path, handler)` |
|
|
1248
|
+
| Route | `routes[]` (`method`, `path`, `operator?`) | `host.routes.register(method, path, handler)` |
|
|
1231
1249
|
| Setting | `settings[]` (`key`, `type`, `default?`) | read via `host.settings.get/set` |
|
|
1232
1250
|
| Attention | `attention: true` | `host.routes.register("GET", "/attention", handler)` -> `{ ok: true, items }` |
|
|
1233
1251
|
|
|
@@ -1293,6 +1311,9 @@ bound for a slot, or `null` when none is. It throws unless the plugin's own mani
|
|
|
1293
1311
|
`{slot, version}` in `contributes.consumes`, and an installed plugin may not resolve `auth` at all. Call
|
|
1294
1312
|
it at request time (inside a route handler or job), never in `activate()`: providers bind during
|
|
1295
1313
|
activation in load order, so a consumer that activates first would read `null` there for good.
|
|
1314
|
+
`host.slots.providerId?.(slot, version)` (#866, optional: a host older than #866 has none) answers the id of the
|
|
1315
|
+
plugin bound to a slot, or `null`, without the provider itself and without a `consumes` entry; an installed
|
|
1316
|
+
plugin may not read `auth`'s.
|
|
1296
1317
|
`ac-artifacts-weft` (`artifacts@1`) is the reference consumer: the second plugin to declare `consumes`
|
|
1297
1318
|
for it, after `artifacts`, which reads `provider-artifacts`' HTTP routes from the browser, and the first to
|
|
1298
1319
|
call `resolve`. `hello` contributes none of the six points above via a slot (nor `attention` -- its own manifest
|
package/dist/attention.d.ts
CHANGED
|
@@ -39,8 +39,9 @@ export interface AttentionItem {
|
|
|
39
39
|
* custom scheme, one that does not parse) renders no link, and the row uses `target` instead, as
|
|
40
40
|
* if `href` were absent. */
|
|
41
41
|
href?: string;
|
|
42
|
-
/** An in-app destination: `widget:<cardId>`
|
|
43
|
-
*
|
|
42
|
+
/** An in-app destination: `widget:<cardId>` scrolls to the home card whose `data-card-id` is
|
|
43
|
+
* `<cardId>` or ends `:<cardId>`, and flashes it (when no such card is on the page the row is
|
|
44
|
+
* plain text), `panel:<pluginId>` navigates to that plugin's panel (its first panel route, or `/<pluginId>`
|
|
44
45
|
* when the console knows none). A value that is not a string is ignored. */
|
|
45
46
|
target?: string;
|
|
46
47
|
/** The exact command to run, or the reason, shown as a tooltip/expander (1.x's `hint`). */
|
package/dist/host-api.d.ts
CHANGED
|
@@ -81,7 +81,9 @@ export interface JobDeclaration {
|
|
|
81
81
|
export interface EventsService {
|
|
82
82
|
/** Calls every subscriber of `type` synchronously, in subscription order, and never awaits one.
|
|
83
83
|
* A subscriber that throws, or returns a promise that rejects, is logged by the host and stops
|
|
84
|
-
* neither the others nor the emitter (#728).
|
|
84
|
+
* neither the others nor the emitter (#728). An event a plugin emits while plugins are still
|
|
85
|
+
* activating also reaches, once every plugin has activated, a subscriber that registered after
|
|
86
|
+
* it, so a handler registered in `activate()` does not depend on load order (#1061). */
|
|
85
87
|
emit(type: string, payload: unknown): void;
|
|
86
88
|
/** Subscribe; returns an unsubscribe function. The handler may be `async` (#728): when it
|
|
87
89
|
* returns a promise (or any thenable) the host attaches a rejection handler, so a rejection is
|
|
@@ -98,7 +100,10 @@ export interface GithubService {
|
|
|
98
100
|
/** The one shared Octokit REST client — consumers make all GitHub calls through this. It
|
|
99
101
|
* authenticates with the console's PAT (`AC_GITHUB_TOKEN`), and `octokit().auth()` returns that
|
|
100
102
|
* token to whichever plugin asks: no `env[]` declaration is needed, and nothing hides it (#784;
|
|
101
|
-
* in-process plugins are trusted code, not sandboxed).
|
|
103
|
+
* in-process plugins are trusted code, not sandboxed). Since #753 the client caps requests at 10 in
|
|
104
|
+
* flight, retries a GET's 5xx (GraphQL and writes are POSTs and are not) and a short secondary limit once, gives each attempt 20 s, and rejects a
|
|
105
|
+
* request at once with `GitHub rate limit exhausted until <ISO>` while a 403/429 has reported that
|
|
106
|
+
* request's budget spent. */
|
|
102
107
|
octokit(): Octokit;
|
|
103
108
|
/** Remaining primary-rate-limit budget from the last response whose three `x-ratelimit-*` headers
|
|
104
109
|
* are all finite numbers (and the reset a valid time), a failed response's included (#784: a 403 or
|
|
@@ -180,6 +185,22 @@ export interface AgentService {
|
|
|
180
185
|
reason: string;
|
|
181
186
|
}>;
|
|
182
187
|
}>;
|
|
188
|
+
/**
|
|
189
|
+
* The relays this plugin opened with `streamJob` in an earlier host process whose end that process
|
|
190
|
+
* never saw (#911): a host restart mid-job. Each is returned once, then forgotten. A process that
|
|
191
|
+
* keeps no relay record (`ac jobs run`) and a host older than #911 return none or lack the method.
|
|
192
|
+
* `taskId` is the earlier process's task id, which no longer exists.
|
|
193
|
+
*/
|
|
194
|
+
unfinishedStreams?(): Promise<UnfinishedStream[]>;
|
|
195
|
+
}
|
|
196
|
+
/** A `streamJob` relay whose end an earlier host process never saw (#911). */
|
|
197
|
+
export interface UnfinishedStream {
|
|
198
|
+
taskId: string;
|
|
199
|
+
jobId: string;
|
|
200
|
+
machineId: string;
|
|
201
|
+
machineName: string;
|
|
202
|
+
label: string;
|
|
203
|
+
openedAt: string;
|
|
183
204
|
}
|
|
184
205
|
/** Proxy to a sidecar plugin's HTTP surface (the host handles health + base URL). */
|
|
185
206
|
export interface SidecarService {
|
|
@@ -229,7 +250,8 @@ export type ActionInputErrorName = "ActionInputError";
|
|
|
229
250
|
* know their callers' payload shapes either. Two providers for the same `slot@version` is a boot
|
|
230
251
|
* error (auth/notify are singletons) -- surfaced by the host-side registry `provide` throws from, not
|
|
231
252
|
* this interface, since this is the plugin's OWN scoped view (pre-bound to its id, so the error can
|
|
232
|
-
* name the real offending plugin without a pluginId parameter here).
|
|
253
|
+
* name the real offending plugin without a pluginId parameter here). `providerId` (#866) reads which
|
|
254
|
+
* plugin holds a binding without exposing it (the admin Security card reads `auth@1`'s). */
|
|
233
255
|
export interface SlotRegistry {
|
|
234
256
|
/** Register this plugin's provider instance for `slot@version`. Throws if that slot already has a
|
|
235
257
|
* provider (from this plugin or another) -- auth/notify are singletons, never a last-write-wins. */
|
|
@@ -244,6 +266,13 @@ export interface SlotRegistry {
|
|
|
244
266
|
* during activation in load order, so a consumer that activates before its provider would read `null`
|
|
245
267
|
* there for good (#97). */
|
|
246
268
|
resolve<T = unknown>(slot: string, version: number): T | null;
|
|
269
|
+
/** The id of the plugin bound to `slot@version`, or `null` when nothing is bound (#866). Never the
|
|
270
|
+
* provider itself, so it opens no way to the impl. Unlike `resolve` it needs no `contributes.consumes`
|
|
271
|
+
* entry. An installed plugin may not read the `auth` slot's (it throws, naming the plugin), as it may
|
|
272
|
+
* not resolve it. Optional: a host older than #866 has no such method, so check for it before calling
|
|
273
|
+
* (`host.slots.providerId?.(slot, version)`). Call it at request time, never in `activate()`, for the
|
|
274
|
+
* same load-order reason as `resolve`. */
|
|
275
|
+
providerId?(slot: string, version: number): string | null;
|
|
247
276
|
}
|
|
248
277
|
/** A logger prefixed with the plugin id. A plain-object first arg is the line's structured fields; an
|
|
249
278
|
* `Error` first arg is logged as its own enumerable properties (`code`, `status`, ...) plus
|
|
@@ -294,6 +323,11 @@ export interface HostApi {
|
|
|
294
323
|
/** #506: see `ConsoleSettingsService`. Optional so an out-of-repo plugin's own test fake still
|
|
295
324
|
* compiles. */
|
|
296
325
|
consoleSettings?: ConsoleSettingsService;
|
|
326
|
+
/** #875: `true` only in the process that serves HTTP and owns the plugins' on-disk state (`main.ts`);
|
|
327
|
+
* absent in a one-shot CLI process (`ac jobs run`), which loads every plugin too. Work that must run
|
|
328
|
+
* once per server boot, and must never run beside a live server (clearing a stale lock file under a
|
|
329
|
+
* git clone, say), is gated on it. Optional so an out-of-repo plugin's own test fake still compiles. */
|
|
330
|
+
serving?: boolean;
|
|
297
331
|
}
|
|
298
332
|
/**
|
|
299
333
|
* The context the host passes to `Plugin.runJob` for one job run (#31). `jobId` selects which
|
package/dist/manifest.d.ts
CHANGED
|
@@ -103,6 +103,14 @@ export interface ActionContribution {
|
|
|
103
103
|
* asks for a second click. */
|
|
104
104
|
confirm?: boolean;
|
|
105
105
|
danger?: boolean;
|
|
106
|
+
/**
|
|
107
|
+
* With the admin kill switch off (`admin_enabled = false`), the host refuses this action (`POST /api/actions/<plugin>/<id>`) with
|
|
108
|
+
* `403 admin disabled`, as it refuses the admin plugin's own surface. Mark an action or route an operator
|
|
109
|
+
* would expect the switch to shut: one that runs a job the job-run route refuses, writes to GitHub or the
|
|
110
|
+
* store on the operator's behalf, or changes the console's configuration. Additive; a host released before
|
|
111
|
+
* this field refuses a manifest that declares it (#867).
|
|
112
|
+
*/
|
|
113
|
+
operator?: true;
|
|
106
114
|
/** The form the shell builds the action's modal from; omit for a no-input action. */
|
|
107
115
|
fields?: ActionField[];
|
|
108
116
|
}
|
|
@@ -149,6 +157,14 @@ export interface RouteContribution {
|
|
|
149
157
|
method: HttpMethod;
|
|
150
158
|
/** Path under the plugin's namespace, e.g. "/feed". */
|
|
151
159
|
path: string;
|
|
160
|
+
/**
|
|
161
|
+
* With the admin kill switch off (`admin_enabled = false`), the host refuses this route for its declared method with
|
|
162
|
+
* `403 admin disabled`, as it refuses the admin plugin's own surface. Mark an action or route an operator
|
|
163
|
+
* would expect the switch to shut: one that runs a job the job-run route refuses, writes to GitHub or the
|
|
164
|
+
* store on the operator's behalf, or changes the console's configuration. Additive; a host released before
|
|
165
|
+
* this field refuses a manifest that declares it (#867).
|
|
166
|
+
*/
|
|
167
|
+
operator?: true;
|
|
152
168
|
}
|
|
153
169
|
/** JSON scalar type of a plugin setting (rendered by Admin). */
|
|
154
170
|
export type SettingType = "string" | "integer" | "number" | "boolean";
|
package/dist/views.d.ts
CHANGED
|
@@ -82,6 +82,10 @@ export type PluginOrigin = "bundled" | "installed";
|
|
|
82
82
|
export interface PluginView {
|
|
83
83
|
id: string;
|
|
84
84
|
enabled: boolean;
|
|
85
|
+
/** The enable state the next restart applies, when it differs from `enabled` (#767): an operator's persisted
|
|
86
|
+
* choice (`POST /api/plugins/:id/enabled`) not yet applied, with `AC_PLUGINS_FORCE_ENABLED` winning as it does
|
|
87
|
+
* at boot. Absent when the two agree. Additive. */
|
|
88
|
+
pendingEnabled?: boolean;
|
|
85
89
|
/** Required even on an errored entry — it is a property of where the plugin came from, not of loading. */
|
|
86
90
|
origin: PluginOrigin;
|
|
87
91
|
error?: string;
|
package/package.json
CHANGED
package/schema.json
CHANGED
|
@@ -235,6 +235,11 @@
|
|
|
235
235
|
"danger": {
|
|
236
236
|
"type": "boolean"
|
|
237
237
|
},
|
|
238
|
+
"operator": {
|
|
239
|
+
"type": "boolean",
|
|
240
|
+
"const": true,
|
|
241
|
+
"description": "With the admin kill switch off (`admin_enabled = false`), the host refuses this action (`POST /api/actions/<plugin>/<id>`) with `403 admin disabled`, as it refuses the admin plugin's own surface. Mark an action or route an operator would expect the switch to shut: one that runs a job the job-run route refuses, writes to GitHub or the store on the operator's behalf, or changes the console's configuration. Additive; a host released before this field refuses a manifest that declares it (#867)."
|
|
242
|
+
},
|
|
238
243
|
"fields": {
|
|
239
244
|
"type": "array",
|
|
240
245
|
"items": {
|
|
@@ -394,6 +399,11 @@
|
|
|
394
399
|
"path": {
|
|
395
400
|
"type": "string",
|
|
396
401
|
"description": "Path under the plugin's namespace, e.g. \"/feed\"."
|
|
402
|
+
},
|
|
403
|
+
"operator": {
|
|
404
|
+
"type": "boolean",
|
|
405
|
+
"const": true,
|
|
406
|
+
"description": "With the admin kill switch off (`admin_enabled = false`), the host refuses this route for its declared method with `403 admin disabled`, as it refuses the admin plugin's own surface. Mark an action or route an operator would expect the switch to shut: one that runs a job the job-run route refuses, writes to GitHub or the store on the operator's behalf, or changes the console's configuration. Additive; a host released before this field refuses a manifest that declares it (#867)."
|
|
397
407
|
}
|
|
398
408
|
},
|
|
399
409
|
"required": [
|