@rackbops/ac-plugin-contract 2.0.0-alpha.41 → 2.0.0-alpha.43
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 +80 -14
- package/dist/host-api.d.ts +10 -0
- package/dist/manifest.d.ts +4 -1
- package/dist/slots.d.ts +44 -3
- package/dist/views.d.ts +9 -0
- package/package.json +3 -3
- package/schema.json +3 -2
package/AUTHORING.md
CHANGED
|
@@ -113,14 +113,21 @@ Rules the host enforces, at validation or load time:
|
|
|
113
113
|
installed plugin errors, naming the bundled owner — and the bundled one keeps running unaffected. This
|
|
114
114
|
is what stops a third-party package from disabling a bundled plugin (e.g. the auth provider, or `admin`
|
|
115
115
|
itself) just by declaring its id. Two bundled plugins sharing an id is still a build/CI mistake and both
|
|
116
|
-
error, the same for two installed plugins with no bundled plugin in the collision.
|
|
116
|
+
error, the same for two installed plugins with no bundled plugin in the collision. **An installed plugin
|
|
117
|
+
may neither provide nor consume the `auth` slot** (#452b, #604): the install gate (`ac plugins install`
|
|
118
|
+
and the admin Plugins card's install, which share it) refuses a manifest whose `contributes.provides` or
|
|
119
|
+
`contributes.consumes` names `auth` (any version), and
|
|
120
|
+
at runtime `slots.provide`/`slots.resolve` refuse that slot to an installed plugin whatever its manifest
|
|
121
|
+
declares. Every other slot, `notify@1` included, is open to an installed plugin.
|
|
117
122
|
- **`hostApiVersion`** must equal the host's `HOST_API_VERSION` (currently `1`); a mismatch is rejected.
|
|
118
123
|
- **`kind: "in-process"`** must declare a `server` entry — the *loader* records an error and skips a plugin that
|
|
119
124
|
doesn't (the schema itself requires only `id`, `hostApiVersion` and `kind`). `contributes` and every sub-field
|
|
120
125
|
is optional in the schema — contribute only the points you use; each contribution's own required fields are
|
|
121
126
|
schema-enforced.
|
|
122
|
-
- **Jobs** (#137): a job's **`timeoutSec`** must be at least `1` (the schema's
|
|
123
|
-
timeout would fire at once and time out any run that awaits real
|
|
127
|
+
- **Jobs** (#137, #580): a job's **`timeoutSec`** must be at least `1` and at most `2147483` (the schema's
|
|
128
|
+
`minimum` and `maximum`; a zero or negative timeout would fire at once and time out any run that awaits real
|
|
129
|
+
I/O, and about 24.8 days is the longest delay Node honours, so a longer or non-finite one would be fired at
|
|
130
|
+
once too), and a job **`id`** must be unique
|
|
124
131
|
*within its plugin* -- two jobs with
|
|
125
132
|
one id are rejected as `duplicate job id "<id>" within the plugin`, like a duplicate panel, card or action id.
|
|
126
133
|
Job ids are plugin-scoped (a job is addressed as `<pluginId>/<jobId>`), so two different plugins may declare the
|
|
@@ -185,10 +192,16 @@ export const createPlugin: CreatePlugin = (host) => ({
|
|
|
185
192
|
click, whether or not the manifest sets `confirm` -- there is no second "Confirm" click. `danger: true` gives
|
|
186
193
|
that primary button the danger style, gives its Ctrl/Cmd+K palette row a danger badge, and stops Enter pressed in
|
|
187
194
|
one of the form's fields from submitting it (a click on the button, or Enter or Space on the focused button,
|
|
188
|
-
still fires; Enter in a text area is a newline). A non-danger action still submits on Enter in
|
|
189
|
-
|
|
190
|
-
not fire
|
|
191
|
-
|
|
195
|
+
still fires; Enter in a text area is a newline). A non-danger action still submits on Enter in a text field;
|
|
196
|
+
Enter on a checkbox, a radio or a select never submits any action (#568), so ticking a label and pressing
|
|
197
|
+
Enter does not fire it: mark an action `danger` if it must not fire from a stray Enter in a text field either.
|
|
198
|
+
After a successful result the primary stays disabled until the modal closes, so an action that succeeded is
|
|
199
|
+
never run again from that modal, however fast it answers (a double-click whose first run resolves inside the
|
|
200
|
+
second click's interval, or a repeated Enter, cannot run it twice). The modal closes itself 1.5 s after the
|
|
201
|
+
success unless the user clicks in it first, and then it stays open, locked, until Cancel or Escape; opening the
|
|
202
|
+
same action again while that locked modal is up reuses it and does not re-run it. After a failed result the
|
|
203
|
+
primary is enabled again and a retry runs, so a double-click whose first run fails fast can run the action
|
|
204
|
+
twice (the second a retry): an action whose failure may have partly applied should tolerate that.
|
|
192
205
|
`confirm` stays a valid manifest flag, so an existing manifest keeps loading, but neither the modal nor the
|
|
193
206
|
palette acts on it.
|
|
194
207
|
- **Job** — implement `runJob(ctx)` and dispatch on `ctx.jobId`. The host runs only jobs you declared in
|
|
@@ -197,7 +210,7 @@ export const createPlugin: CreatePlugin = (host) => ({
|
|
|
197
210
|
newest 200 runs of that job (per machine, for a `runOn: "agent"` job) and drops runs older than 30 days,
|
|
198
211
|
though never a job's (or machine's) newest run, so `host.jobs.lastRun` still answers for a job that runs
|
|
199
212
|
less often than monthly. A job that runs every minute therefore no longer grows the run history without
|
|
200
|
-
bound. `ctx.signal` aborts at `timeoutSec` seconds (
|
|
213
|
+
bound. `ctx.signal` aborts at `timeoutSec` seconds (1 to 2147483, #137, #580). `runOn: "host"` runs your `runJob`
|
|
201
214
|
once, in the host process.
|
|
202
215
|
`runOn: "agent"` (#51) also runs your `runJob` in the host process — the agent itself runs no plugin
|
|
203
216
|
code — but once per currently paired machine, with `ctx.machine` set to `{ id, name }`; do the
|
|
@@ -432,6 +445,31 @@ strip of the panel uncovered between the header and the overlay; and the panel i
|
|
|
432
445
|
the bar collapses. The layout arithmetic and the whole-pixel rounding were measured in desktop Chromium at
|
|
433
446
|
375 px; the phone behaviours in this list are derived from the CSS, not observed on a phone.
|
|
434
447
|
|
|
448
|
+
### A frontend that bundles a dependency (#98)
|
|
449
|
+
|
|
450
|
+
"Only the import-map externals" is what keeps one React on the page; it does not forbid a plugin from
|
|
451
|
+
bundling a library the import map does not carry. `plugins/ac-artifacts-weft` inlines
|
|
452
|
+
`@lepid-labs/weft-embed` (a framework-free reader, 1.15 MB) and its stylesheet into `dist/ui.js`, and its
|
|
453
|
+
`rollup.ui.config.mjs` is the reference. (The rollup configs of `issues-classic` and `ac-auth-cf-access`
|
|
454
|
+
inline a dependency into their SERVER bundle, and every other plugin's UI config inlines nothing; none of
|
|
455
|
+
them is a model for this.) What a bundled dependency changes:
|
|
456
|
+
|
|
457
|
+
- **One file.** The host serves exactly the manifest's `frontend` and never a sibling, so a stylesheet is a
|
|
458
|
+
string in the bundle that the panel injects as a `<style>`, once (the config's `inlineCss` plugin turns
|
|
459
|
+
`import css from "pkg/style.css"` into that string), never a `<link>` to a `.css` file.
|
|
460
|
+
- **Externals stay bare, everything else is inlined** (`@rollup/plugin-node-resolve`). The dependency goes in
|
|
461
|
+
`dependencies`, pinned exactly if its rendering is part of what you ship, and is absent at runtime: the
|
|
462
|
+
image carries `dist/` and no `node_modules`.
|
|
463
|
+
- **A library without types** gets a `src/<lib>.d.ts` that declares the module, listed in the UI tsconfigs.
|
|
464
|
+
- **Guard what shipped with a bundle test that runs the real build** (`src/bundle.test.ts` is the example):
|
|
465
|
+
anchor the import regex to the start of a line (inlined code carries doc comments such as ` * import
|
|
466
|
+
{unified} from 'unified'`), assert that no bare specifier outside the externals survives and that the
|
|
467
|
+
library is not one, that nothing needs `require(`, `import.meta` or `eval`, what the bundle exports, and a
|
|
468
|
+
size budget.
|
|
469
|
+
- **A stylesheet you did not write must not restyle the shell.** Check its selectors for what is scoped (a
|
|
470
|
+
parser over the CSS is enough: weft's 0.2.0 rules are all scoped to its own root), and have the smoke spec
|
|
471
|
+
compare the shell's computed styles with and without it in the page.
|
|
472
|
+
|
|
435
473
|
### The `@ac/host` browser module
|
|
436
474
|
|
|
437
475
|
`@ac/host` (typed as `BrowserHostApi` in `@ac/contract`) is the one shared module instance the shell and every
|
|
@@ -554,10 +592,13 @@ same-origin iframe (the Artifacts report preview does) forwards the hotkey: bind
|
|
|
554
592
|
iframe's `contentDocument` (skip a frame whose `contentDocument` is null or throws: cross-origin), and on Ctrl
|
|
555
593
|
or Cmd plus `k` with no Alt and no Shift, `preventDefault()` and dispatch `new CustomEvent("ac:quickcast-key")`
|
|
556
594
|
on the parent `window`. The shell opens the palette under the keystroke's own guard: only when no other dialog
|
|
557
|
-
is open, and an event while the palette is already open
|
|
558
|
-
Escape). Remove the listener when the frame goes away. Closing the palette with Escape
|
|
559
|
-
|
|
560
|
-
|
|
595
|
+
is open, and an event while the palette is already open leaves it open (closing stays with Ctrl/Cmd+K and
|
|
596
|
+
Escape). Remove the listener when the frame goes away. Closing the palette with Escape or Ctrl/Cmd+K again
|
|
597
|
+
returns focus to the frame element (#588): the native dialog's own restore does not put it back there for a
|
|
598
|
+
palette opened from inside a frame (observed in Chromium), so the shell remembers the element that had focus
|
|
599
|
+
when the event arrived and focuses it again when focus is nowhere useful after the close (on `body`, or
|
|
600
|
+
still on a node of the palette that just closed). Which element inside the frame's own document has focus
|
|
601
|
+
afterwards is the browser's call.
|
|
561
602
|
|
|
562
603
|
`fetch` makes a same-origin, credentialed, CSRF-aware request to a host/plugin route — a relative `path`
|
|
563
604
|
resolves against the console origin; a cross-origin absolute URL throws rather than being followed. A panel
|
|
@@ -995,7 +1036,25 @@ integer `rank` and the shell only sorts by it. The rule, in priority order:
|
|
|
995
1036
|
Ties break the same way, so the order is total. `order` is a hint the operator can always override with
|
|
996
1037
|
`tab_order`, so leave it off unless your panel belongs at the front (the Board declares `10`, Issues `20`,
|
|
997
1038
|
which puts them right after Home). `Home` is always first and is not orderable. A panel that lost a
|
|
998
|
-
contested nav slot still has no tab, and still appears in quickcast at its ranked position
|
|
1039
|
+
contested nav slot still has no tab, and still appears in quickcast at its ranked position (once the
|
|
1040
|
+
operator's choice has flipped the slot, the position the other panel's tab would hold: the two swap ranks, #629).
|
|
1041
|
+
|
|
1042
|
+
**Contesting a nav slot (#65, #100).** A panel's `nav` names the slot it competes for the tab of. A slot with
|
|
1043
|
+
one panel is owned by it. When two or more panels from plugins that loaded without error name the same slot,
|
|
1044
|
+
the host picks one owner per request: the operator's choice, stored in the `nav.<slot>.panel` host setting
|
|
1045
|
+
(written through `POST /api/nav-slot` by the header's switcher, which renders only for a contested slot; an
|
|
1046
|
+
`AC_NAV_<SLOT>_PANEL` environment variable, when set, wins over it and pins the slot: the switcher for it is
|
|
1047
|
+
disabled, naming the variable, and `POST /api/nav-slot` answers `409`), else the smallest panel id. A panel
|
|
1048
|
+
that does not own its slot has no tab but keeps its route and its quickcast
|
|
1049
|
+
entry. The slot's tab stays where the default owner's would be, whichever panel owns it (#629), so a
|
|
1050
|
+
flip changes what the tab shows, not where it sits (a `tab_order` that names the chosen owner places it
|
|
1051
|
+
where the operator said). So declaring a `nav` another panel already has changes two things for every operator: the header gains
|
|
1052
|
+
the switcher, and the default owner is whichever panel id sorts first (an id that sorts after the existing
|
|
1053
|
+
owner leaves the default alone; one that sorts before it takes the tab from every operator who has not made a
|
|
1054
|
+
choice, and, since #629, also moves the slot's tab for an operator who has: its position is where the default
|
|
1055
|
+
owner's would be, and the default owner is the smallest id). The bundled example is the `artifacts` slot: the native viewer's `viewer` (`plugins/artifacts`, route
|
|
1056
|
+
`/artifacts`, the default) and the weft reader's `weft-reader` (`plugins/ac-artifacts-weft`, route
|
|
1057
|
+
`/artifacts-weft`).
|
|
999
1058
|
|
|
1000
1059
|
**`isolation` (#124).** A panel may declare `isolation: "iframe"`: the value is reserved (design §4) and the
|
|
1001
1060
|
manifest accepts it, but nothing implements it in 2.0 and the host ignores it -- panels run in-process today,
|
|
@@ -1015,7 +1074,14 @@ refuses to activate a consumer whose slot has no provider, and says why (`resolv
|
|
|
1015
1074
|
`packages/host/src/plugins/slots.ts`, re-exported from `plugins/index.ts`). `ac-auth-cf-access`
|
|
1016
1075
|
(`auth@1`) and `ac-notify-discord` (`notify@1`) are the reference *providers*; see their own
|
|
1017
1076
|
`src/server.ts` for `host.slots.provide(slot, version, impl)`, and `design.md` §4 for the slot
|
|
1018
|
-
contract itself.
|
|
1077
|
+
contract itself. The consumer half is `host.slots.resolve<T>(slot, version)` (#97): the provider
|
|
1078
|
+
bound for a slot, or `null` when none is. It throws unless the plugin's own manifest lists the exact
|
|
1079
|
+
`{slot, version}` in `contributes.consumes`, and an installed plugin may not resolve `auth` at all. Call
|
|
1080
|
+
it at request time (inside a route handler or job), never in `activate()`: providers bind during
|
|
1081
|
+
activation in load order, so a consumer that activates first would read `null` there for good.
|
|
1082
|
+
`ac-artifacts-weft` (`artifacts@1`) is the reference consumer: the second plugin to declare `consumes`
|
|
1083
|
+
for it, after `artifacts`, which reads `provider-artifacts`' HTTP routes from the browser, and the first to
|
|
1084
|
+
call `resolve`. `hello` contributes none of the six points above via a slot (nor `attention` -- its own manifest
|
|
1019
1085
|
declares panels/cards/actions/jobs/routes/settings only, matching the intro above) -- it's the
|
|
1020
1086
|
reference for those six, not for slot provision or the attention rail; `plugins/scans` and
|
|
1021
1087
|
`plugins/overview` are the worked examples for `contributes.attention` (§8 above).
|
package/dist/host-api.d.ts
CHANGED
|
@@ -175,6 +175,16 @@ export interface SlotRegistry {
|
|
|
175
175
|
/** Register this plugin's provider instance for `slot@version`. Throws if that slot already has a
|
|
176
176
|
* provider (from this plugin or another) -- auth/notify are singletons, never a last-write-wins. */
|
|
177
177
|
provide(slot: string, version: number, impl: unknown): void;
|
|
178
|
+
/** The provider bound for `slot@version`, cast to `T` (a slot's interface in `slots.ts`, e.g.
|
|
179
|
+
* `ArtifactsProvider`), or `null` when nothing is bound. A plugin may resolve only a slot its
|
|
180
|
+
* manifest lists in `contributes.consumes`; any other throws, naming the plugin and the slot. An
|
|
181
|
+
* installed plugin may not resolve the `auth` slot at all, declared or not (as it may not provide it);
|
|
182
|
+
* the install gate also refuses a manifest that declares `provides` or `consumes` for `auth` (#604),
|
|
183
|
+
* so this call-time refusal is what remains for a plugin staged before that check.
|
|
184
|
+
* Call it at request time (inside a route handler or job), never in `activate()`: providers bind
|
|
185
|
+
* during activation in load order, so a consumer that activates before its provider would read `null`
|
|
186
|
+
* there for good (#97). */
|
|
187
|
+
resolve<T = unknown>(slot: string, version: number): T | null;
|
|
178
188
|
}
|
|
179
189
|
/** A logger prefixed with the plugin id. */
|
|
180
190
|
export interface LogService {
|
package/dist/manifest.d.ts
CHANGED
|
@@ -112,8 +112,11 @@ export interface JobContribution {
|
|
|
112
112
|
schedule: string;
|
|
113
113
|
/**
|
|
114
114
|
* Per-run timeout in seconds. Must be at least 1: a zero or negative value would fire the timeout at
|
|
115
|
-
* once, timing out any run that awaits real I/O (#137).
|
|
115
|
+
* once, timing out any run that awaits real I/O (#137). And at most 2147483, about 24.8 days: Node
|
|
116
|
+
* clamps a longer or non-finite `setTimeout` delay to 1 ms, which fires it at once, the same failure
|
|
117
|
+
* (#580).
|
|
116
118
|
* @minimum 1
|
|
119
|
+
* @maximum 2147483
|
|
117
120
|
*/
|
|
118
121
|
timeoutSec: number;
|
|
119
122
|
runOn: JobRunOn;
|
package/dist/slots.d.ts
CHANGED
|
@@ -99,19 +99,60 @@ export interface IssuesProvider {
|
|
|
99
99
|
/** A repo's own labels, independent of the standard (E5 #58). */
|
|
100
100
|
repoLabels(repo: RepoRef): Promise<Label[]>;
|
|
101
101
|
}
|
|
102
|
-
/** A node in the artifacts report tree (v1 seam; E6 #68 enriches).
|
|
102
|
+
/** A node in the artifacts report tree (v1 seam; E6 #68 enriches).
|
|
103
|
+
*
|
|
104
|
+
* A `report` node's `id` is its markdown source's path relative to the store's `artifacts/` directory
|
|
105
|
+
* (`Code-Reviews/2026-09-01/summary.md`), the id `source`/`read` take; a report with no markdown source
|
|
106
|
+
* (a rendered `.html` nothing in the store produced) falls back to its `href`. A `category` node's id is
|
|
107
|
+
* `category:<name>` and a `run` node's `run:<category>/<run>`; neither names a readable source. */
|
|
103
108
|
export interface ArtifactNode {
|
|
104
109
|
id: string;
|
|
105
110
|
title: string;
|
|
106
111
|
kind: "category" | "run" | "report";
|
|
107
112
|
children?: ArtifactNode[];
|
|
113
|
+
/** A report's markdown source path (the same relative path its `id` carries), absent when the report has
|
|
114
|
+
* no markdown source. A node without it is a rendered `.html` only: `source`/`read` then answer that
|
|
115
|
+
* rendered HTML, not markdown, so a consumer that expects markdown must skip such a node. Report nodes
|
|
116
|
+
* only (#97). */
|
|
117
|
+
md?: string;
|
|
118
|
+
/** A report's viewer path: the rendered `.html` once rendered (`provider-artifacts`' `GET /render/<href>`
|
|
119
|
+
* serves it from its docroot), else the `.md` source path, which that route does not serve (it reads the
|
|
120
|
+
* docroot only; read the text through `source`). Report nodes only (#97). */
|
|
121
|
+
href?: string;
|
|
122
|
+
}
|
|
123
|
+
/** One `search` result: a section of one report. */
|
|
124
|
+
export interface ArtifactSearchHit {
|
|
125
|
+
/** The id of the report the hit is in, built the same way as `ArtifactNode.id` for a report that has a
|
|
126
|
+
* markdown source (search only reads markdown), so a hit opens through `source`/`read`. A report the
|
|
127
|
+
* tree leaves out, such as a rendered `index` page, can still surface here. */
|
|
128
|
+
id: string;
|
|
129
|
+
/** The report's title as the search reads it (its first level-1 heading, else its file name with the
|
|
130
|
+
* extension dropped and `-`/`_` shown as spaces). For a rendered report that can differ from the tree
|
|
131
|
+
* node's title, which comes from the rendered page's `<title>`. */
|
|
132
|
+
title: string;
|
|
133
|
+
/** The matching section's heading id inside the rendered report, without a leading `#`. Absent when the
|
|
134
|
+
* report is not rendered, when its rendered heading count disagrees with the source's, or when the
|
|
135
|
+
* section has no id (a hit on the report's preamble, or an empty `id` attribute). */
|
|
136
|
+
anchor?: string;
|
|
137
|
+
/** A short excerpt around the match. */
|
|
138
|
+
snippet: string;
|
|
108
139
|
}
|
|
109
|
-
/** `artifacts@1`: tree
|
|
140
|
+
/** `artifacts@1`: the report tree, one report's source by id, and ranked search over the markdown
|
|
141
|
+
* sources. A consumer reaches it in-process through `HostApi.slots.resolve("artifacts", 1)` (#97). */
|
|
110
142
|
export interface ArtifactsProvider {
|
|
111
143
|
/** The report tree. */
|
|
112
144
|
tree(): Promise<ArtifactNode[]>;
|
|
113
|
-
/** Raw source of one report by id
|
|
145
|
+
/** Raw source of one report by id; throws exactly when `source` would answer `null`. */
|
|
114
146
|
read(id: string): Promise<string>;
|
|
147
|
+
/** Ranked cross-report search over the store's markdown sources, at most 50 hits (the cap `GET /search`
|
|
148
|
+
* has); `CONTEXT.md`, `CONTENT.md`, `Archive/` and `.sync-inbox/` are not searched. An empty array for
|
|
149
|
+
* an empty query. */
|
|
150
|
+
search(q: string): Promise<ArtifactSearchHit[]>;
|
|
151
|
+
/** Raw source of one report by id: `null` when no `.md` or `.html` resolves at that path inside the
|
|
152
|
+
* store (or the path escapes it). The tree, not `source`, says what counts as a report: a path the tree
|
|
153
|
+
* skips (an `index` page whose html exists, `CONTEXT.md`, anything under `Archive/`) still answers its
|
|
154
|
+
* text here. */
|
|
155
|
+
source(id: string): Promise<string | null>;
|
|
115
156
|
}
|
|
116
157
|
/** An authenticated identity. */
|
|
117
158
|
export interface Identity {
|
package/dist/views.d.ts
CHANGED
|
@@ -14,6 +14,15 @@ export interface PluginPanelView {
|
|
|
14
14
|
nav?: string;
|
|
15
15
|
export: string;
|
|
16
16
|
navOwner: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Present (and only ever `"env"` today) on every contestant of a contested nav slot whose choice of owner
|
|
19
|
+
* an `AC_NAV_<SLOT>_PANEL` environment variable fixes (#628), so the shell can render that slot's switcher
|
|
20
|
+
* control disabled with the reason, and a script can expect `409` from `POST /api/nav-slot` for it. The
|
|
21
|
+
* variable pins the slot whatever its value is (one naming no contestant leaves the default owner, and the
|
|
22
|
+
* slot is still pinned). Absent otherwise (never `undefined`-valued). Optional, so a shell built before it
|
|
23
|
+
* ignores it.
|
|
24
|
+
*/
|
|
25
|
+
navPinnedBy?: "env";
|
|
17
26
|
/**
|
|
18
27
|
* This panel's position among every panel in the response (#472): a non-negative integer, lower
|
|
19
28
|
* first, resolved by the host per request from the operator's `tab_order` setting, then each
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rackbops/ac-plugin-contract",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.43",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "The artifact-console plugin ABI for out-of-repo plugin authors: HostApi, the acPlugin manifest contribution points, the generated manifest JSON Schema, and the @ac/host ambient browser declaration.",
|
|
@@ -35,9 +35,9 @@
|
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@ac/contract": "0.0.0",
|
|
38
|
-
"@types/node": "^26.
|
|
38
|
+
"@types/node": "^26.6.3",
|
|
39
39
|
"typescript": "^7.0.0",
|
|
40
|
-
"vitest": "^5.0.
|
|
40
|
+
"vitest": "^5.0.2"
|
|
41
41
|
},
|
|
42
42
|
"scripts": {
|
|
43
43
|
"build": "node build.mjs",
|
package/schema.json
CHANGED
|
@@ -348,8 +348,9 @@
|
|
|
348
348
|
},
|
|
349
349
|
"timeoutSec": {
|
|
350
350
|
"type": "number",
|
|
351
|
-
"description": "Per-run timeout in seconds. Must be at least 1: a zero or negative value would fire the timeout at once, timing out any run that awaits real I/O (#137).",
|
|
352
|
-
"minimum": 1
|
|
351
|
+
"description": "Per-run timeout in seconds. Must be at least 1: a zero or negative value would fire the timeout at once, timing out any run that awaits real I/O (#137). And at most 2147483, about 24.8 days: Node clamps a longer or non-finite `setTimeout` delay to 1 ms, which fires it at once, the same failure (#580).",
|
|
352
|
+
"minimum": 1,
|
|
353
|
+
"maximum": 2147483
|
|
353
354
|
},
|
|
354
355
|
"runOn": {
|
|
355
356
|
"$ref": "#/definitions/JobRunOn"
|