@cloudhal/agent-runtime 0.0.0-stage → 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/LICENSE +202 -0
- package/README.md +8 -2
- package/dist/src/approvals.d.ts +127 -0
- package/dist/src/approvals.js +346 -0
- package/dist/src/audit.d.ts +75 -0
- package/dist/src/audit.js +209 -0
- package/dist/src/index.d.ts +13 -0
- package/dist/src/index.js +13 -0
- package/dist/src/modes.d.ts +124 -0
- package/dist/src/modes.js +279 -0
- package/dist/src/ownership.d.ts +76 -0
- package/dist/src/ownership.js +200 -0
- package/dist/src/policy.d.ts +197 -0
- package/dist/src/policy.js +508 -0
- package/dist/src/roles.d.ts +110 -0
- package/dist/src/roles.js +292 -0
- package/dist/src/site-access.d.ts +35 -0
- package/dist/src/site-access.js +81 -0
- package/dist/src/skills.d.ts +47 -0
- package/dist/src/skills.js +151 -0
- package/dist/src/taxonomy.d.ts +41 -0
- package/dist/src/taxonomy.js +78 -0
- package/package.json +44 -3
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { Actor } from "@cloudhal/core-contracts";
|
|
2
|
+
import type { SqlDriver } from "@cloudhal/storage";
|
|
3
|
+
import type { AutoCheck } from "./modes.js";
|
|
4
|
+
import type { ApprovalCause } from "./policy.js";
|
|
5
|
+
export interface AuditEntry {
|
|
6
|
+
at: string;
|
|
7
|
+
actor: Actor;
|
|
8
|
+
siteId?: string | undefined;
|
|
9
|
+
tool: string;
|
|
10
|
+
decision: "allow" | "deny" | "approval-required";
|
|
11
|
+
reason?: string | undefined;
|
|
12
|
+
skill?: string | undefined;
|
|
13
|
+
input?: unknown;
|
|
14
|
+
outcome?: "ok" | "error" | "pending" | undefined;
|
|
15
|
+
snapshotId?: string | undefined;
|
|
16
|
+
durationMs?: number | undefined;
|
|
17
|
+
/** Agent mode the call ran under (ADR-0015). */
|
|
18
|
+
mode?: string | undefined;
|
|
19
|
+
/** Why the call waited for a person (`approval-required` rows). */
|
|
20
|
+
cause?: ApprovalCause | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* The `auto`-mode check behind a publish: `lowRisk` on an `allow` row means it went live without
|
|
23
|
+
* a person; on an `approval-required` row it says why it waited.
|
|
24
|
+
*/
|
|
25
|
+
autoCheck?: AutoCheck | undefined;
|
|
26
|
+
/** The call ran under a time-boxed elevation (ADR-0016): the key's own mode and the end. */
|
|
27
|
+
elevation?: {
|
|
28
|
+
from: string;
|
|
29
|
+
until: string;
|
|
30
|
+
} | undefined;
|
|
31
|
+
/** RFC-0008 §5.1, §11.7: the automation run a call belongs to (`audit.run_id`). */
|
|
32
|
+
runId?: string | undefined;
|
|
33
|
+
/** RFC-0008 §5.1: the release a row is about (`audit.release_id`). */
|
|
34
|
+
releaseId?: string | undefined;
|
|
35
|
+
/** RFC-0008 §11.5: the automation whose key made the call (`audit.automation_id`). */
|
|
36
|
+
automationId?: string | undefined;
|
|
37
|
+
}
|
|
38
|
+
export interface AuditRow extends AuditEntry {
|
|
39
|
+
id: number;
|
|
40
|
+
}
|
|
41
|
+
/** Inputs are stored as JSON; oversized ones become a valid `{ truncated, preview }` object. */
|
|
42
|
+
export declare function auditInput(input: unknown): string | null;
|
|
43
|
+
/** Append-only record of every policy decision and tool call (table `audit`, migration 0003). */
|
|
44
|
+
export declare class AuditLog {
|
|
45
|
+
#private;
|
|
46
|
+
constructor(sql: SqlDriver);
|
|
47
|
+
/**
|
|
48
|
+
* The INSERT of one audit row as a statement, for writes that must put it in their own atomic
|
|
49
|
+
* `batch()`, last (RFC-0007 §4.4: a comment write and its audit row land together or not at all).
|
|
50
|
+
*/
|
|
51
|
+
static statement(entry: AuditEntry): {
|
|
52
|
+
sql: string;
|
|
53
|
+
params: (string | number | null)[];
|
|
54
|
+
};
|
|
55
|
+
record(entry: AuditEntry): Promise<number>;
|
|
56
|
+
list(filter?: {
|
|
57
|
+
siteId?: string | undefined;
|
|
58
|
+
actorId?: string | undefined;
|
|
59
|
+
/** Only rows by one of these actors (a person's own rows and their keys' rows). */
|
|
60
|
+
actorIds?: readonly string[] | undefined;
|
|
61
|
+
/** Only deployment rows (no site: membership, invites, keys). */
|
|
62
|
+
deployment?: boolean | undefined;
|
|
63
|
+
/** Only rows of this tool (or event, e.g. `member.role`). */
|
|
64
|
+
tool?: string | undefined;
|
|
65
|
+
/** Only rows at or after this ISO time. */
|
|
66
|
+
since?: string | undefined;
|
|
67
|
+
/** Only rows older than this id (cursor pagination, newest first). */
|
|
68
|
+
before?: number | undefined;
|
|
69
|
+
/** RFC-0008 §11.7: only the rows of one automation run. */
|
|
70
|
+
runId?: string | undefined;
|
|
71
|
+
/** RFC-0008: only the rows about one release. */
|
|
72
|
+
releaseId?: string | undefined;
|
|
73
|
+
limit?: number | undefined;
|
|
74
|
+
}): Promise<AuditRow[]>;
|
|
75
|
+
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
const MAX_INPUT = 4000;
|
|
42
|
+
|
|
43
|
+
/** Inputs are stored as JSON; oversized ones become a valid `{ truncated, preview }` object. */
|
|
44
|
+
export function auditInput(input ) {
|
|
45
|
+
if (input === undefined) return null;
|
|
46
|
+
const json = JSON.stringify(input) ?? "null";
|
|
47
|
+
if (json.length <= MAX_INPUT) return json;
|
|
48
|
+
return JSON.stringify({
|
|
49
|
+
truncated: true,
|
|
50
|
+
bytes: json.length,
|
|
51
|
+
preview: json.slice(0, MAX_INPUT - 200),
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function parseAuditInput(raw ) {
|
|
56
|
+
if (!raw) return undefined;
|
|
57
|
+
try {
|
|
58
|
+
return JSON.parse(raw);
|
|
59
|
+
} catch {
|
|
60
|
+
// rows written before inputs were truncated safely
|
|
61
|
+
return { truncated: true, preview: raw };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function parseJson (raw ) {
|
|
66
|
+
try {
|
|
67
|
+
return JSON.parse(raw) ;
|
|
68
|
+
} catch {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Append-only record of every policy decision and tool call (table `audit`, migration 0003). */
|
|
74
|
+
export class AuditLog {
|
|
75
|
+
#sql ;
|
|
76
|
+
constructor(sql ) {
|
|
77
|
+
this.#sql = sql;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* The INSERT of one audit row as a statement, for writes that must put it in their own atomic
|
|
82
|
+
* `batch()`, last (RFC-0007 §4.4: a comment write and its audit row land together or not at all).
|
|
83
|
+
*/
|
|
84
|
+
static statement(entry ) {
|
|
85
|
+
return {
|
|
86
|
+
sql: "INSERT INTO audit (at, actor_kind, actor_id, actor_name, site_id, tool, decision, reason, skill, input, outcome, snapshot_id, duration_ms, mode, cause, auto_check, elevation, run_id, release_id, automation_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
87
|
+
params: [
|
|
88
|
+
entry.at,
|
|
89
|
+
entry.actor.kind,
|
|
90
|
+
entry.actor.id,
|
|
91
|
+
entry.actor.name ?? null,
|
|
92
|
+
entry.siteId ?? null,
|
|
93
|
+
entry.tool,
|
|
94
|
+
entry.decision,
|
|
95
|
+
entry.reason ?? null,
|
|
96
|
+
entry.skill ?? null,
|
|
97
|
+
auditInput(entry.input),
|
|
98
|
+
entry.outcome ?? null,
|
|
99
|
+
entry.snapshotId ?? null,
|
|
100
|
+
entry.durationMs ?? null,
|
|
101
|
+
entry.mode ?? null,
|
|
102
|
+
entry.cause ?? null,
|
|
103
|
+
entry.autoCheck ? JSON.stringify(entry.autoCheck) : null,
|
|
104
|
+
entry.elevation ? JSON.stringify(entry.elevation) : null,
|
|
105
|
+
entry.runId ?? null,
|
|
106
|
+
entry.releaseId ?? null,
|
|
107
|
+
entry.automationId ?? null,
|
|
108
|
+
],
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async record(entry ) {
|
|
113
|
+
const { sql, params } = AuditLog.statement(entry);
|
|
114
|
+
const r = await this.#sql.run(sql, params);
|
|
115
|
+
return r.lastRowId ?? 0;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async list(
|
|
119
|
+
filter
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
= {},
|
|
138
|
+
) {
|
|
139
|
+
const where = [];
|
|
140
|
+
const params = [];
|
|
141
|
+
if (filter.before !== undefined) {
|
|
142
|
+
where.push("id < ?");
|
|
143
|
+
params.push(filter.before);
|
|
144
|
+
}
|
|
145
|
+
if (filter.siteId) {
|
|
146
|
+
where.push("site_id = ?");
|
|
147
|
+
params.push(filter.siteId);
|
|
148
|
+
}
|
|
149
|
+
if (filter.actorId) {
|
|
150
|
+
where.push("actor_id = ?");
|
|
151
|
+
params.push(filter.actorId);
|
|
152
|
+
}
|
|
153
|
+
if (filter.actorIds) {
|
|
154
|
+
// An empty list matches nothing; the caller bounds the list (own keys), capped here at 80.
|
|
155
|
+
const ids = filter.actorIds.slice(0, 80);
|
|
156
|
+
where.push(ids.length ? `actor_id IN (${ids.map(() => "?").join(", ")})` : "0");
|
|
157
|
+
params.push(...ids);
|
|
158
|
+
}
|
|
159
|
+
if (filter.deployment) where.push("site_id IS NULL");
|
|
160
|
+
if (filter.tool) {
|
|
161
|
+
where.push("tool = ?");
|
|
162
|
+
params.push(filter.tool);
|
|
163
|
+
}
|
|
164
|
+
if (filter.since) {
|
|
165
|
+
where.push("at >= ?");
|
|
166
|
+
params.push(filter.since);
|
|
167
|
+
}
|
|
168
|
+
if (filter.runId) {
|
|
169
|
+
where.push("run_id = ?");
|
|
170
|
+
params.push(filter.runId);
|
|
171
|
+
}
|
|
172
|
+
if (filter.releaseId) {
|
|
173
|
+
where.push("release_id = ?");
|
|
174
|
+
params.push(filter.releaseId);
|
|
175
|
+
}
|
|
176
|
+
params.push(filter.limit ?? 100);
|
|
177
|
+
const rows = await this.#sql.all (
|
|
178
|
+
`SELECT * FROM audit ${where.length ? `WHERE ${where.join(" AND ")}` : ""} ORDER BY id DESC LIMIT ?`,
|
|
179
|
+
params,
|
|
180
|
+
);
|
|
181
|
+
return rows.map((r) => ({
|
|
182
|
+
id: r.id ,
|
|
183
|
+
at: r.at ,
|
|
184
|
+
actor: {
|
|
185
|
+
kind: r.actor_kind ,
|
|
186
|
+
id: r.actor_id ,
|
|
187
|
+
...(r.actor_name ? { name: r.actor_name } : {}),
|
|
188
|
+
},
|
|
189
|
+
siteId: (r.site_id ) ?? undefined,
|
|
190
|
+
tool: r.tool ,
|
|
191
|
+
decision: r.decision ,
|
|
192
|
+
reason: (r.reason ) ?? undefined,
|
|
193
|
+
skill: (r.skill ) ?? undefined,
|
|
194
|
+
input: parseAuditInput(r.input ),
|
|
195
|
+
outcome: (r.outcome ) ?? undefined,
|
|
196
|
+
snapshotId: (r.snapshot_id ) ?? undefined,
|
|
197
|
+
durationMs: (r.duration_ms ) ?? undefined,
|
|
198
|
+
mode: (r.mode ) ?? undefined,
|
|
199
|
+
...(r.cause ? { cause: r.cause } : {}),
|
|
200
|
+
...(r.auto_check ? { autoCheck: parseJson (r.auto_check ) } : {}),
|
|
201
|
+
...(r.elevation
|
|
202
|
+
? { elevation: parseJson (r.elevation ) }
|
|
203
|
+
: {}),
|
|
204
|
+
...(r.run_id ? { runId: r.run_id } : {}),
|
|
205
|
+
...(r.release_id ? { releaseId: r.release_id } : {}),
|
|
206
|
+
...(r.automation_id ? { automationId: r.automation_id } : {}),
|
|
207
|
+
}));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @cloudhal/agent-runtime — what governs agents: the policy engine (ADR-0004), the skill loader
|
|
3
|
+
* (ADR-0005) and the audit log. The MCP server and the API call into this package; it never calls them.
|
|
4
|
+
*/
|
|
5
|
+
export * from "./approvals.js";
|
|
6
|
+
export * from "./audit.js";
|
|
7
|
+
export * from "./modes.js";
|
|
8
|
+
export * from "./ownership.js";
|
|
9
|
+
export * from "./policy.js";
|
|
10
|
+
export * from "./roles.js";
|
|
11
|
+
export * from "./site-access.js";
|
|
12
|
+
export * from "./skills.js";
|
|
13
|
+
export * from "./taxonomy.js";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @cloudhal/agent-runtime — what governs agents: the policy engine (ADR-0004), the skill loader
|
|
3
|
+
* (ADR-0005) and the audit log. The MCP server and the API call into this package; it never calls them.
|
|
4
|
+
*/
|
|
5
|
+
export * from "./approvals.js";
|
|
6
|
+
export * from "./audit.js";
|
|
7
|
+
export * from "./modes.js";
|
|
8
|
+
export * from "./ownership.js";
|
|
9
|
+
export * from "./policy.js";
|
|
10
|
+
export * from "./roles.js";
|
|
11
|
+
export * from "./site-access.js";
|
|
12
|
+
export * from "./skills.js";
|
|
13
|
+
export * from "./taxonomy.js";
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent modes (ADR-0015): how independently an agent works, chosen per API key by its owner —
|
|
3
|
+
* the same idea as an IDE agent's permission modes. A mode never grants scopes; it narrows what
|
|
4
|
+
* the key's scopes allow and decides where a person must approve.
|
|
5
|
+
*/
|
|
6
|
+
import type { Scope } from "./policy.js";
|
|
7
|
+
export type AgentMode = "plan" | "manual" | "edits" | "auto" | "bypass";
|
|
8
|
+
/** In order of increasing autonomy. */
|
|
9
|
+
export declare const AGENT_MODES: readonly AgentMode[];
|
|
10
|
+
/** What existing keys and new keys without a choice get. */
|
|
11
|
+
export declare const DEFAULT_AGENT_MODE: AgentMode;
|
|
12
|
+
/** Modes a remote (HTTP, API key) agent may hold. `bypass` is for local stdio only. */
|
|
13
|
+
export declare const REMOTE_AGENT_MODES: readonly AgentMode[];
|
|
14
|
+
export declare function isAgentMode(value: unknown): value is AgentMode;
|
|
15
|
+
/** The scopes a key needs to be useful in a mode (offered by default when creating one). */
|
|
16
|
+
export declare function scopesForMode(mode: AgentMode): Scope[];
|
|
17
|
+
/** Limits for an `auto`-mode publish that goes live without a person (see `isLowRiskPublish`). */
|
|
18
|
+
export declare const AUTO_PUBLISH_LIMIT = 20;
|
|
19
|
+
export interface PendingChange {
|
|
20
|
+
id: string;
|
|
21
|
+
kind: string;
|
|
22
|
+
}
|
|
23
|
+
/** Why `auto` mode did or did not let a publish through, as data (Studio words it itself). */
|
|
24
|
+
export type AutoCheckCode = "low-risk" | "removes-content" | "nothing" | "structure" | "too-many"
|
|
25
|
+
/** RFC-0004 §6.3: media that does not ride along with an article change. */
|
|
26
|
+
| "media"
|
|
27
|
+
/** RFC-0008 §10.7 (D9): the release would run after the key's window (elevation end or 24 h). */
|
|
28
|
+
| "outside-window"
|
|
29
|
+
/** RFC-0019 §2.4: an article gains a correction, an editorial statement a person signs off. */
|
|
30
|
+
| "correction";
|
|
31
|
+
/** The `auto`-mode check behind a publish (stored on audit rows and approvals, ADR-0015). */
|
|
32
|
+
export interface AutoCheck {
|
|
33
|
+
/** `low-risk-release`: the same check applied to a release's items (RFC-0008 §10.3). */
|
|
34
|
+
rule: "low-risk-publish" | "low-risk-release";
|
|
35
|
+
lowRisk: boolean;
|
|
36
|
+
code: AutoCheckCode;
|
|
37
|
+
/** Pending changes the publish would carry (added + edited + removed). */
|
|
38
|
+
changes: number;
|
|
39
|
+
/** `AUTO_PUBLISH_LIMIT` at the time of the check. */
|
|
40
|
+
limit: number;
|
|
41
|
+
}
|
|
42
|
+
/** English wording of a check, for agents and audit reasons. */
|
|
43
|
+
export declare function describeAutoCheck(check: AutoCheck): string;
|
|
44
|
+
/**
|
|
45
|
+
* Low risk = only articles added or edited, nothing removed, no pages, menus, site settings,
|
|
46
|
+
* taxonomies, terms or redirects touched (RFC-0002 §6.4, RFC-0003 §6.4), and at most `AUTO_PUBLISH_LIMIT` changes. Anything
|
|
47
|
+
* else waits for a person even in `auto`.
|
|
48
|
+
*
|
|
49
|
+
* Media (RFC-0004 §6.3, answer 1): an added asset whose id is in `ridingAssets` (a new raster
|
|
50
|
+
* image used only by articles in the same publish; the store decides) goes out with them and does
|
|
51
|
+
* not count toward the limit. Any other asset change is `media` and waits for a person.
|
|
52
|
+
*
|
|
53
|
+
* Corrections (RFC-0019 §2.4): an entry in `corrections` (it adds a correction to its live
|
|
54
|
+
* version; the store decides) makes the publish wait for a person.
|
|
55
|
+
*/
|
|
56
|
+
export declare function checkAutoPublish(diff: {
|
|
57
|
+
added: PendingChange[];
|
|
58
|
+
changed: {
|
|
59
|
+
after: PendingChange;
|
|
60
|
+
}[];
|
|
61
|
+
removed: PendingChange[];
|
|
62
|
+
}, options?: {
|
|
63
|
+
ridingAssets?: ReadonlySet<string>;
|
|
64
|
+
corrections?: ReadonlySet<string>;
|
|
65
|
+
}): AutoCheck;
|
|
66
|
+
/**
|
|
67
|
+
* Time-boxed elevation (ADR-0016): the owner lets a key work in a more autonomous mode until
|
|
68
|
+
* `until`; afterwards it is back in its own mode without anyone doing anything (the effective mode
|
|
69
|
+
* is computed on every call).
|
|
70
|
+
*/
|
|
71
|
+
export interface ModeElevation {
|
|
72
|
+
mode: AgentMode;
|
|
73
|
+
/** ISO time the elevation ends. */
|
|
74
|
+
until: string;
|
|
75
|
+
/** Actor id of the person who granted it. */
|
|
76
|
+
grantedBy: string;
|
|
77
|
+
grantedAt: string;
|
|
78
|
+
/** Why, in the owner's words (shown in Studio and the audit). */
|
|
79
|
+
reason?: string | undefined;
|
|
80
|
+
}
|
|
81
|
+
/** Shortest and longest elevation an owner can grant. */
|
|
82
|
+
export declare const ELEVATION_MIN_MINUTES = 5;
|
|
83
|
+
export declare const ELEVATION_MAX_MINUTES: number;
|
|
84
|
+
/** `a` lets the agent do more on its own than `b` (AGENT_MODES order). */
|
|
85
|
+
export declare function isMoreAutonomous(a: AgentMode, b: AgentMode): boolean;
|
|
86
|
+
/**
|
|
87
|
+
* Why an elevation request is refused, or undefined when it is acceptable: a remote mode more
|
|
88
|
+
* autonomous than the key's own, for between `ELEVATION_MIN_MINUTES` and `ELEVATION_MAX_MINUTES`.
|
|
89
|
+
*/
|
|
90
|
+
export declare function elevationProblem(base: AgentMode, mode: AgentMode, minutes: number): string | undefined;
|
|
91
|
+
/** The elevation that applies right now, if any (expired or no-longer-higher ones do not). */
|
|
92
|
+
export declare function activeElevation(base: AgentMode, elevation: ModeElevation | undefined, now: Date): ModeElevation | undefined;
|
|
93
|
+
/** How long before an elevation ends that owners and the agent are told (Studio notifications, `get_mode`). */
|
|
94
|
+
export declare const ELEVATION_NOTICE_MINUTES = 10;
|
|
95
|
+
/** Whole minutes left until `until` (rounded up; 0 once it has ended). */
|
|
96
|
+
export declare function minutesLeft(until: string, now: Date): number;
|
|
97
|
+
/** True during the last `ELEVATION_NOTICE_MINUTES` of an elevation. */
|
|
98
|
+
export declare function endsSoon(until: string, now: Date): boolean;
|
|
99
|
+
/** The longest an `auto`-mode release may wait without a person (RFC-0008 §10.7, D9). */
|
|
100
|
+
export declare const AUTO_RELEASE_WINDOW_HOURS = 24;
|
|
101
|
+
/** A release item as the low-risk check sees it (RFC-0008 §10.3). */
|
|
102
|
+
export interface ReleaseItemRisk {
|
|
103
|
+
kind: "set" | "remove";
|
|
104
|
+
nodeId: string;
|
|
105
|
+
nodeKind: string;
|
|
106
|
+
/** The node is live today (the item has a base). */
|
|
107
|
+
live: boolean;
|
|
108
|
+
/** Ids the target references (assets that ride with an article). */
|
|
109
|
+
deps?: readonly string[];
|
|
110
|
+
/** RFC-0019 §2.4: the target adds a correction to the live version. */
|
|
111
|
+
corrects?: boolean;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* `checkAutoPublish` applied to a release (RFC-0008 §10.3): articles only, nothing removed, at most
|
|
115
|
+
* `AUTO_PUBLISH_LIMIT` changes; a new asset rides along when an article item of the same release
|
|
116
|
+
* references it.
|
|
117
|
+
*/
|
|
118
|
+
export declare function checkAutoRelease(items: readonly ReleaseItemRisk[]): AutoCheck;
|
|
119
|
+
/**
|
|
120
|
+
* The latest an `auto`-mode release may run and still go ahead without a person (RFC-0008 §10.7,
|
|
121
|
+
* D9): the end of the key's elevation (when `auto` comes from one) or 24 hours from now, whichever
|
|
122
|
+
* comes first.
|
|
123
|
+
*/
|
|
124
|
+
export declare function autoReleaseWindowEnds(now: Date, elevationUntil?: string): string;
|