@intentius/chant 0.123.0 → 0.124.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.
Files changed (62) hide show
  1. package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
  2. package/dist/config-import.d.ts +1 -1
  3. package/dist/lint/policy-import.d.ts +1 -1
  4. package/dist/notify-verify.js +76 -0
  5. package/dist/notify.js +307 -0
  6. package/dist/op/__fixtures__/decide-stub-backend.d.ts +1 -1
  7. package/dist/op/index.d.ts +4 -4
  8. package/dist/op/index.d.ts.map +1 -1
  9. package/dist/op/op-waves-run.d.ts +134 -2
  10. package/dist/op/op-waves-run.d.ts.map +1 -1
  11. package/dist/op/op-waves.d.ts +14 -0
  12. package/dist/op/op-waves.d.ts.map +1 -1
  13. package/dist/pr-forge.d.ts +30 -3
  14. package/dist/pr-forge.d.ts.map +1 -1
  15. package/dist/workspace/conformance/__fixtures__/reference-broker.d.ts +1 -1
  16. package/dist/workspace/conformance/broker.d.ts +1 -1
  17. package/package.json +14 -2
  18. package/src/cli/commands/check-lexicon.ts +3 -1
  19. package/src/cli/commands/lint-post-synth.e2e.test.ts +117 -0
  20. package/src/cli/commands/lint.test.ts +4 -109
  21. package/src/cli/discovery-skip.e2e.test.ts +18 -3
  22. package/src/config-import.ts +1 -1
  23. package/src/discovery/sandbox/run.test.ts +1 -1
  24. package/src/lint/policy-import.ts +1 -1
  25. package/src/notify-plain-node.e2e.test.ts +68 -0
  26. package/src/op/__fixtures__/decide-stub-backend.ts +1 -1
  27. package/src/op/index.ts +18 -2
  28. package/src/op/op-waves-run.ts +454 -83
  29. package/src/op/op-waves.test.ts +191 -1
  30. package/src/op/op-waves.ts +29 -1
  31. package/src/pr-forge.test.ts +76 -1
  32. package/src/pr-forge.ts +52 -8
  33. package/src/workspace/conformance/__fixture__/decisions/decision.kind.mjs +1 -1
  34. package/src/workspace/conformance/__fixtures__/reference-broker.ts +1 -1
  35. package/src/workspace/conformance/broker.ts +1 -1
  36. package/src/workspace/records-write.e2e.test.ts +1 -1
  37. /package/src/audit/{catalog.test.ts → catalog.e2e.test.ts} +0 -0
  38. /package/src/audit/{discover.test.ts → discover.e2e.test.ts} +0 -0
  39. /package/src/audit/{remote-discovery.test.ts → remote-discovery.e2e.test.ts} +0 -0
  40. /package/src/audit/{rules-doc.test.ts → rules-doc.e2e.test.ts} +0 -0
  41. /package/src/{bench.test.ts → bench.e2e.test.ts} +0 -0
  42. /package/src/cli/commands/{check-lexicon-doc-drift.test.ts → check-lexicon-doc-drift.e2e.test.ts} +0 -0
  43. /package/src/cli/commands/{import.test.ts → import.e2e.test.ts} +0 -0
  44. /package/src/cli/{component-security-boundary.test.ts → component-security-boundary.e2e.test.ts} +0 -0
  45. /package/src/cli/mcp/{workspace-tools.test.ts → workspace-tools.e2e.test.ts} +0 -0
  46. /package/src/cli/{security-boundary.test.ts → security-boundary.e2e.test.ts} +0 -0
  47. /package/src/cli/{serve-mcp-workspace.test.ts → serve-mcp-workspace.e2e.test.ts} +0 -0
  48. /package/src/cli/{static-config-read.test.ts → static-config-read.e2e.test.ts} +0 -0
  49. /package/src/codegen/{json-schema-to-ts.test.ts → json-schema-to-ts.e2e.test.ts} +0 -0
  50. /package/src/{lexicon-seams.test.ts → lexicon-seams.e2e.test.ts} +0 -0
  51. /package/src/meta/{declared-imports.test.ts → declared-imports.e2e.test.ts} +0 -0
  52. /package/src/op/{command-gate.test.ts → command-gate.e2e.test.ts} +0 -0
  53. /package/src/op/{steward-beside.test.ts → steward-beside.e2e.test.ts} +0 -0
  54. /package/src/op/{steward-points.test.ts → steward-points.e2e.test.ts} +0 -0
  55. /package/src/workspace/{checks.test.ts → checks.e2e.test.ts} +0 -0
  56. /package/src/workspace/{intent-reviews.test.ts → intent-reviews.e2e.test.ts} +0 -0
  57. /package/src/workspace/{profiles.test.ts → profiles.e2e.test.ts} +0 -0
  58. /package/src/workspace/{read-contract.test.ts → read-contract.e2e.test.ts} +0 -0
  59. /package/src/workspace/{records-contract.test.ts → records-contract.e2e.test.ts} +0 -0
  60. /package/src/workspace/{records-since.test.ts → records-since.e2e.test.ts} +0 -0
  61. /package/src/workspace/{records-write.test.ts → records-write-commands.e2e.test.ts} +0 -0
  62. /package/src/workspace/{work-size.test.ts → work-size.e2e.test.ts} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"check-lexicon.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/check-lexicon.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAInD,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;CACpB;AAyBD,gEAAgE;AAChE;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,eAAe,EAAE,MAAM,GAAG,MAAM,CAIlE;AAuBD;;;;;;;;;;;GAWG;AACH,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,aAAa,GAAG,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,CAyB/F;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,aAAa,GAAG,SAAS,EACjC,GAAG,EAAE,MAAM,GACV;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAanC;AAED;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAazE;AAID;;GAEG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CA8lBpE;AAqBD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAmDzE"}
1
+ {"version":3,"file":"check-lexicon.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/check-lexicon.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAInD,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;CACpB;AAyBD,gEAAgE;AAChE;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,eAAe,EAAE,MAAM,GAAG,MAAM,CAIlE;AAuBD;;;;;;;;;;;GAWG;AACH,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,aAAa,GAAG,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,CAyB/F;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,aAAa,GAAG,SAAS,EACjC,GAAG,EAAE,MAAM,GACV;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAanC;AAED;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAazE;AAID;;GAEG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAgmBpE;AAqBD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAmDzE"}
@@ -6,7 +6,7 @@
6
6
  * — one narrow module whose only job is "execute project-authored code here",
7
7
  * so that the question "did any project code run in this process?" has one
8
8
  * place to look and one place to instrument (see
9
- * `examples/sandbox-execution-boundary.test.ts`, which spies on both).
9
+ * `examples/sandbox-execution-boundary.e2e.test.ts`, which spies on both).
10
10
  *
11
11
  * Callers must not `import()` a config file directly. `./config-sandbox.ts`
12
12
  * decides whether a given load is allowed to come through here at all: under
@@ -7,7 +7,7 @@
7
7
  * `importConfigModule` (`chant.config.ts`): one narrow module whose only job is
8
8
  * "execute project-authored code here", so that "did any project code run in
9
9
  * this process?" has one place to look and one place to instrument — see
10
- * `examples/sandbox-execution-boundary.test.ts`, which spies on all three.
10
+ * `examples/sandbox-execution-boundary.e2e.test.ts`, which spies on all three.
11
11
  *
12
12
  * Before chant #1131 `./policy.ts` called `await import(resolved)` inline,
13
13
  * which is why the corpus boundary gate could not see it even after #1113 put
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Signing and checking a notify event (#3761). This module needs only
3
+ * `node:crypto`, so a receiver can import it alone and bundle nothing else:
4
+ *
5
+ * ```ts
6
+ * import { verifyWebhook } from "@intentius/chant/notify-verify";
7
+ *
8
+ * const checked = verifyWebhook({ body, headers: req.headers, key: process.env.CHANT_WEBHOOK_KEY });
9
+ * if (!checked.ok) return reply(401, checked.reason);
10
+ * handle(checked.event);
11
+ * ```
12
+ *
13
+ * The signature is HMAC-SHA256, keyed with the shared secret, over
14
+ * `<timestamp>.<body>`: the timestamp header (seconds since the epoch), a
15
+ * dot, and the request body exactly as sent. It travels as
16
+ * `x-chant-signature: sha256=<hex>`. Signing the timestamp with the body lets
17
+ * a receiver refuse a delivery replayed later; {@link verifyWebhook} refuses
18
+ * one more than five minutes from now by default. This is SQL Yodeler's
19
+ * scheme (#3756), with the header prefix a product can keep as its own.
20
+ */
21
+ import { createHmac, timingSafeEqual } from "node:crypto";
22
+ /** The header names' default prefix. A product passes its own (`x-yodel`, `x-terragucci`) to keep its receivers working. */
23
+ export const DEFAULT_HEADER_PREFIX = "x-chant";
24
+ /** The four headers a delivery carries, under `prefix`. */
25
+ export function webhookHeaderNames(prefix = DEFAULT_HEADER_PREFIX) {
26
+ const p = prefix.toLowerCase();
27
+ return { signature: `${p}-signature`, timestamp: `${p}-timestamp`, event: `${p}-event`, delivery: `${p}-delivery` };
28
+ }
29
+ /** How far from now a delivery's timestamp {@link verifyWebhook} still accepts, in seconds. */
30
+ export const DEFAULT_TOLERANCE_SECONDS = 300;
31
+ /** `sha256=<hex>` of `<timestamp>.<body>`, keyed with `key`. */
32
+ export function signWebhook(key, timestamp, body) {
33
+ return `sha256=${createHmac("sha256", key).update(`${String(timestamp)}.${body}`, "utf8").digest("hex")}`;
34
+ }
35
+ function header(headers, name) {
36
+ if (typeof headers.get === "function")
37
+ return headers.get(name) ?? undefined;
38
+ for (const [k, v] of Object.entries(headers)) {
39
+ if (k.toLowerCase() !== name)
40
+ continue;
41
+ return Array.isArray(v) ? v[0] : v;
42
+ }
43
+ return undefined;
44
+ }
45
+ /**
46
+ * Whether a delivery was signed with `key` and is recent: the signature
47
+ * header matches (compared in constant time) and the timestamp is within the
48
+ * tolerance either side of now. On success, the parsed event.
49
+ */
50
+ export function verifyWebhook(input) {
51
+ if (!input.key)
52
+ return { ok: false, reason: "no key to verify with" };
53
+ const names = webhookHeaderNames(input.prefix);
54
+ const body = typeof input.body === "string" ? input.body : Buffer.from(input.body).toString("utf8");
55
+ const signature = header(input.headers, names.signature);
56
+ const timestamp = header(input.headers, names.timestamp);
57
+ if (!signature)
58
+ return { ok: false, reason: `no ${names.signature} header` };
59
+ if (!timestamp || !/^\d+$/.test(timestamp))
60
+ return { ok: false, reason: `no ${names.timestamp} header, or not a number of seconds` };
61
+ const expected = Buffer.from(signWebhook(input.key, timestamp, body));
62
+ const given = Buffer.from(signature);
63
+ if (given.length !== expected.length || !timingSafeEqual(given, expected))
64
+ return { ok: false, reason: "the signature does not match" };
65
+ const now = input.now ?? Math.floor(Date.now() / 1000);
66
+ const tolerance = input.toleranceSeconds ?? DEFAULT_TOLERANCE_SECONDS;
67
+ if (Math.abs(now - Number(timestamp)) > tolerance)
68
+ return { ok: false, reason: `the timestamp is more than ${String(tolerance)}s from now` };
69
+ try {
70
+ return { ok: true, event: JSON.parse(body) };
71
+ }
72
+ catch {
73
+ return { ok: false, reason: "the body is not JSON" };
74
+ }
75
+ }
76
+ //# sourceMappingURL=notify-verify.js.map
package/dist/notify.js ADDED
@@ -0,0 +1,307 @@
1
+ /**
2
+ * Notify (#3761): tell people that a wave waits for an approval, that a run
3
+ * was refused or failed, or that drift was found.
4
+ *
5
+ * terragucci and SQL Yodeler each carried a copy of this (#3756). chant owns
6
+ * the event, its signing and the Slack and Teams posting; a product decides
7
+ * when to send and what its summary says. One event shape,
8
+ * `chant.notify/v1` (`./notify.schema.json`), carries what both send: the
9
+ * wave or environment, the targets concerned, the plan digest, the command
10
+ * that approves it and the run's link.
11
+ *
12
+ * ```ts
13
+ * import { makeNotifyEvent, sendNotify } from "@intentius/chant/notify";
14
+ *
15
+ * const event = makeNotifyEvent({ source: "sql-yodeler", event: "waiting", summary: "prod waits for an approval", targets: ["0007_add_index"], digest, approve: { command } });
16
+ * await sendNotify({ webhook: "YODEL_WEBHOOK_URL", key: "YODEL_WEBHOOK_KEY", slack: "SLACK_WEBHOOK_URL" }, event);
17
+ * ```
18
+ *
19
+ * The configuration names environment variables, never URLs or keys, and
20
+ * nothing here prints a URL or a key. The generic webhook gets the event as
21
+ * JSON, signed over `<timestamp>.<body>` (`./notify-verify.ts`); it is never
22
+ * posted unsigned. Slack and Teams incoming webhooks get a message rendered
23
+ * from the event, unsigned, since neither checks a signature. A delivery that
24
+ * fails is logged and returned; {@link sendNotify} never throws, so a
25
+ * caller's exit code never depends on it.
26
+ *
27
+ * Bundle-safe: this module reaches only `node:crypto` and
28
+ * `./notify-verify.ts` (`./notify-bundle.test.ts`).
29
+ */
30
+ import { createHash, randomUUID } from "node:crypto";
31
+ import { DEFAULT_HEADER_PREFIX, signWebhook, webhookHeaderNames } from "./notify-verify.js";
32
+ export { DEFAULT_HEADER_PREFIX, DEFAULT_TOLERANCE_SECONDS, signWebhook, verifyWebhook, webhookHeaderNames } from "./notify-verify.js";
33
+ /** The event's `schema` value. A field is only ever added within a version. */
34
+ export const NOTIFY_SCHEMA = "chant.notify/v1";
35
+ /** The JSON Schema's `$id`. */
36
+ export const NOTIFY_SCHEMA_ID = "https://intentius.io/chant/schemas/notify/v1/notify.schema.json";
37
+ /** What happened. */
38
+ export const NOTIFY_EVENTS = ["waiting", "refused", "failed", "drift"];
39
+ /** The CI run, from its environment: GitHub Actions, Forgejo Actions or GitLab CI. */
40
+ export function ciRun(env) {
41
+ const out = {};
42
+ if (env.CI_JOB_URL)
43
+ out.url = env.CI_JOB_URL;
44
+ else if (env.GITHUB_SERVER_URL && env.GITHUB_REPOSITORY && env.GITHUB_RUN_ID)
45
+ out.url = `${env.GITHUB_SERVER_URL}/${env.GITHUB_REPOSITORY}/actions/runs/${env.GITHUB_RUN_ID}`;
46
+ else if (env.CI_PIPELINE_URL)
47
+ out.url = env.CI_PIPELINE_URL;
48
+ if (env.GITLAB_CI === "true")
49
+ out.forge = "gitlab";
50
+ else if (env.GITEA_ACTIONS === "true" || env.FORGEJO_ACTIONS === "true")
51
+ out.forge = "forgejo";
52
+ else if (env.GITHUB_ACTIONS === "true")
53
+ out.forge = "github";
54
+ const repository = env.GITHUB_REPOSITORY || env.CI_PROJECT_PATH;
55
+ if (repository)
56
+ out.repository = repository;
57
+ const commit = env.GITHUB_SHA || env.CI_COMMIT_SHA;
58
+ if (commit)
59
+ out.commit = commit;
60
+ return out;
61
+ }
62
+ /**
63
+ * A whole event: the fields, with the schema, the time, the CI run (the
64
+ * product's own `run` fields win) and an id. The id is a hash over the
65
+ * source, project, event, wave, environment and digest (else the drift
66
+ * fingerprint, else the commit), so a re-run sends the same id; with none of
67
+ * the three it is random.
68
+ */
69
+ export function makeNotifyEvent(fields, options = {}) {
70
+ const env = options.env ?? process.env;
71
+ const { id: given, project, targets, run: own, ...rest } = fields;
72
+ const run = { ...ciRun(env), ...(own ?? {}) };
73
+ const resolvedProject = project ?? run.repository ?? "this project";
74
+ const key = fields.digest ?? fields.drift?.fingerprint ?? run.commit;
75
+ const id = given ??
76
+ (key
77
+ ? createHash("sha256").update(JSON.stringify([fields.source, resolvedProject, fields.event, fields.wave?.number ?? null, fields.environment ?? null, key])).digest("hex")
78
+ : randomUUID());
79
+ return {
80
+ schema: NOTIFY_SCHEMA,
81
+ id,
82
+ at: (options.now ?? new Date()).toISOString(),
83
+ ...rest,
84
+ project: resolvedProject,
85
+ targets: targets ?? [],
86
+ ...(Object.keys(run).length > 0 ? { run } : {}),
87
+ };
88
+ }
89
+ const HEADLINE = {
90
+ waiting: "waits for an approval",
91
+ refused: "was refused",
92
+ failed: "failed",
93
+ drift: "drifted",
94
+ };
95
+ /** The first line of every chat message: `<source>: <summary>`, or what happened to what when there is no summary. */
96
+ export function notifyHeadline(e) {
97
+ if (e.summary)
98
+ return `${e.source}: ${e.summary}`;
99
+ const what = e.wave ? `wave ${e.wave.number}${e.wave.name ? ` (${e.wave.name})` : ""} of ${e.project}` : e.environment ? `${e.environment} in ${e.project}` : e.project;
100
+ return `${e.source}: ${what} ${HEADLINE[e.event]}`;
101
+ }
102
+ /** The label and value lines both chat messages show, in order. */
103
+ function facts(e) {
104
+ const out = [];
105
+ if (e.approve?.review)
106
+ out.push({ title: "Review and approve", value: `pull request ${e.approve.review.pr}, then run it again`, link: e.approve.review.url });
107
+ if (e.drift) {
108
+ if (e.drift.changed.length > 0)
109
+ out.push({ title: "Drifted", value: e.drift.changed.join(", ") });
110
+ if (e.drift.missing?.length)
111
+ out.push({ title: "Missing", value: e.drift.missing.join(", ") });
112
+ if (e.drift.failed?.length)
113
+ out.push({ title: "Not refreshed", value: e.drift.failed.join(", ") });
114
+ }
115
+ else if (e.targets.length > 0)
116
+ out.push({ title: "Targets", value: e.targets.join(", ") });
117
+ if (e.digest)
118
+ out.push({ title: "Digest", value: e.digest });
119
+ if (e.approve)
120
+ out.push({ title: e.approve.review ? "Or approve" : "Approve", value: e.approve.command });
121
+ if (e.reason)
122
+ out.push({ title: "Reason", value: e.reason });
123
+ if (e.run?.url)
124
+ out.push({ title: "Run", value: e.run.url, link: e.run.url });
125
+ if (e.reportUrl)
126
+ out.push({ title: "Report", value: e.reportUrl, link: e.reportUrl });
127
+ return out;
128
+ }
129
+ /** A Slack incoming webhook's body: one mrkdwn text, with the approve command as code and the links as links. */
130
+ export function slackMessage(e) {
131
+ const lines = [`*${notifyHeadline(e)}*`];
132
+ for (const f of facts(e)) {
133
+ if (f.title === "Review and approve")
134
+ lines.push(`${f.title}: <${f.link}|pull request ${e.approve.review.pr}>, then run it again`);
135
+ else if (f.title === "Approve" || f.title === "Or approve" || f.title === "Digest")
136
+ lines.push(`${f.title}: \`${f.value}\``);
137
+ else if (f.link)
138
+ lines.push(`${f.title}: <${f.link}>`);
139
+ else
140
+ lines.push(`${f.title}: ${f.value}`);
141
+ }
142
+ return { text: lines.join("\n") };
143
+ }
144
+ /** A Teams incoming webhook's body (a Workflows webhook): one Adaptive Card, with buttons that open the review page and the run. */
145
+ export function teamsMessage(e) {
146
+ const all = facts(e);
147
+ const actions = [
148
+ ...(e.approve?.review ? [{ type: "Action.OpenUrl", title: "Review and approve", url: e.approve.review.url }] : []),
149
+ ...(e.run?.url ? [{ type: "Action.OpenUrl", title: "Open the run", url: e.run.url }] : []),
150
+ ];
151
+ return {
152
+ type: "message",
153
+ attachments: [
154
+ {
155
+ contentType: "application/vnd.microsoft.card.adaptive",
156
+ contentUrl: null,
157
+ content: {
158
+ $schema: "http://adaptivecards.io/schemas/adaptive-card.json",
159
+ type: "AdaptiveCard",
160
+ version: "1.4",
161
+ body: [
162
+ { type: "TextBlock", text: notifyHeadline(e), weight: "Bolder", wrap: true },
163
+ { type: "FactSet", facts: all.map((f) => ({ title: f.title, value: f.link && f.title === "Review and approve" ? `${f.value}: ${f.link}` : f.value })) },
164
+ ],
165
+ ...(actions.length > 0 ? { actions } : {}),
166
+ },
167
+ },
168
+ ],
169
+ };
170
+ }
171
+ const VARIABLE = /^[A-Za-z_][A-Za-z0-9_]*$/;
172
+ const SETTINGS = ["webhook", "key", "slack", "teams", "events"];
173
+ /** The first problem with a {@link NotifyConfig}, or undefined. `where` prefixes the message. */
174
+ export function notifyConfigProblem(value, where = "notify") {
175
+ if (typeof value !== "object" || value === null || Array.isArray(value))
176
+ return `${where}: expected { webhook, key, slack, teams, events }`;
177
+ const v = value;
178
+ for (const k of Object.keys(v))
179
+ if (!SETTINGS.includes(k))
180
+ return `${where}.${k}: unknown setting (expected ${SETTINGS.join(", ")})`;
181
+ for (const k of ["webhook", "key", "slack", "teams"]) {
182
+ const n = v[k];
183
+ if (n === undefined)
184
+ continue;
185
+ if (typeof n !== "string" || !VARIABLE.test(n)) {
186
+ const what = k === "key" ? "signing key" : "URL";
187
+ return `${where}.${k}: the name of the environment variable that holds the ${what}, never the ${k === "key" ? "key" : "URL"} itself`;
188
+ }
189
+ }
190
+ if (v.webhook !== undefined && v.key === undefined)
191
+ return `${where}.key: a webhook needs a signing key; name the variable that holds it`;
192
+ if (v.key !== undefined && v.webhook === undefined)
193
+ return `${where}.webhook: a key is for a webhook; name the variable that holds its URL`;
194
+ if (v.webhook === undefined && v.slack === undefined && v.teams === undefined)
195
+ return `${where}: name a webhook (with its key), a slack or a teams URL variable`;
196
+ if (v.events !== undefined && !(Array.isArray(v.events) && v.events.every((e) => NOTIFY_EVENTS.includes(e)))) {
197
+ return `${where}.events: a list of ${NOTIFY_EVENTS.join(", ")}`;
198
+ }
199
+ return undefined;
200
+ }
201
+ /** The environment variables a configuration names, for a pipeline to map its secrets into. */
202
+ export function notifyVariables(config) {
203
+ if (!config)
204
+ return [];
205
+ return [config.webhook, config.key, config.slack, config.teams].filter((n) => typeof n === "string" && n !== "");
206
+ }
207
+ /** The request {@link sendNotify} posts to the webhook: the event as JSON, signed with `key` over `<timestamp>.<body>`. */
208
+ export function webhookRequest(event, key, options = {}) {
209
+ const body = JSON.stringify(event);
210
+ const now = options.now ?? Math.floor(Date.now() / 1000);
211
+ const names = webhookHeaderNames(options.prefix ?? DEFAULT_HEADER_PREFIX);
212
+ return {
213
+ body,
214
+ headers: {
215
+ "content-type": "application/json",
216
+ "user-agent": options.userAgent ?? event.source,
217
+ [names.event]: event.event,
218
+ [names.delivery]: event.id,
219
+ [names.timestamp]: String(now),
220
+ [names.signature]: signWebhook(key, now, body),
221
+ },
222
+ };
223
+ }
224
+ /** A failure's message, with every URL it names cut out. */
225
+ function redact(message, urls) {
226
+ let out = message;
227
+ for (const u of urls)
228
+ if (u)
229
+ out = out.split(u).join("<url>");
230
+ return out.replace(/https?:\/\/\S+/g, "<url>");
231
+ }
232
+ async function post(fetchFn, url, request, attempts, sleep) {
233
+ let last = "not attempted";
234
+ for (let i = 0; i < attempts; i++) {
235
+ if (i > 0)
236
+ await sleep(500 * i);
237
+ try {
238
+ const res = await fetchFn(url, { method: "POST", ...request, signal: AbortSignal.timeout(10000) });
239
+ if (res.ok)
240
+ return undefined;
241
+ last = `HTTP ${String(res.status)}`;
242
+ // A 4xx will not change on a retry: the receiver refused the event.
243
+ if (res.status < 500)
244
+ return last;
245
+ }
246
+ catch (e) {
247
+ last = redact(e instanceof Error ? e.message : String(e), [url]);
248
+ }
249
+ }
250
+ return last;
251
+ }
252
+ const LABEL = { webhook: "the webhook", slack: "Slack", teams: "Teams" };
253
+ /**
254
+ * Sends `event` to what `config` names, reading the URLs and the key from
255
+ * the environment at send time. Never throws: each delivery's outcome is
256
+ * returned, and a failed one is logged without its URL.
257
+ */
258
+ export async function sendNotify(config, event, options = {}) {
259
+ if (!config || !event)
260
+ return [];
261
+ if (config.events && !config.events.includes(event.event))
262
+ return [];
263
+ const env = options.env ?? process.env;
264
+ const fetchFn = options.fetch ?? ((url, init) => globalThis.fetch(url, init));
265
+ const attempts = Math.max(1, options.attempts ?? 3);
266
+ const sleep = options.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
267
+ const log = options.log ?? ((line) => process.stderr.write(line + "\n"));
268
+ const out = [];
269
+ const deliver = async (target, variable, request) => {
270
+ let d;
271
+ try {
272
+ const url = env[variable];
273
+ if (!url)
274
+ d = { target, ok: false, detail: `${variable} is not set` };
275
+ else {
276
+ const r = request();
277
+ if (typeof r === "string")
278
+ d = { target, ok: false, detail: r };
279
+ else {
280
+ const failed = await post(fetchFn, url, r, attempts, sleep);
281
+ d = { target, ok: failed === undefined, detail: failed ?? "delivered" };
282
+ }
283
+ }
284
+ }
285
+ catch (e) {
286
+ d = { target, ok: false, detail: redact(e instanceof Error ? e.message : String(e), [env[variable] ?? ""]) };
287
+ }
288
+ if (!d.ok)
289
+ log(`notify: the ${event.event} event was not delivered to ${LABEL[target]}: ${d.detail}`);
290
+ out.push(d);
291
+ };
292
+ const json = (body) => ({ headers: { "content-type": "application/json" }, body: JSON.stringify(body) });
293
+ if (config.webhook) {
294
+ await deliver("webhook", config.webhook, () => {
295
+ const key = config.key ? env[config.key] : undefined;
296
+ if (!key)
297
+ return `${config.key ?? "the key variable"} is not set, and an event is never sent unsigned`;
298
+ return webhookRequest(event, key, { ...(options.now !== undefined ? { now: options.now } : {}), ...(options.prefix ? { prefix: options.prefix } : {}), ...(options.userAgent ? { userAgent: options.userAgent } : {}) });
299
+ });
300
+ }
301
+ if (config.slack)
302
+ await deliver("slack", config.slack, () => json((options.render?.slack ?? slackMessage)(event)));
303
+ if (config.teams)
304
+ await deliver("teams", config.teams, () => json((options.render?.teams ?? teamsMessage)(event)));
305
+ return out;
306
+ }
307
+ //# sourceMappingURL=notify.js.map
@@ -15,7 +15,7 @@
15
15
  * forces one (429 or 529, say). Every request it read is kept in `requests`.
16
16
  *
17
17
  * It lives under `__fixtures__` because it listens on a port, and no module
18
- * chant runs does (#2657, `test/no-listener.test.ts`): only tests start it.
18
+ * chant runs does (#2657, `test/no-listener.e2e.test.ts`): only tests start it.
19
19
  */
20
20
  import type { WireAnswer } from "../../workspace/points.js";
21
21
  import { type BackendRequest } from "../decide-backend.js";
@@ -20,10 +20,10 @@ export { discoverOps } from "./discover.js";
20
20
  export type { DiscoveredOp, OpDiscoveryResult } from "./discover.js";
21
21
  export { generateOpsPipeline, generateOpWavesPipeline, withOpSchedules } from "./generate-pipeline.js";
22
22
  export type { GenerateOpsPipelineResult, GenerateOpWavesPipelineResult } from "./generate-pipeline.js";
23
- export { OP_WAVE_DEFAULT_APPLY, OP_WAVE_GATE_POLICIES, OP_WAVE_HOOK_MOMENTS, assertOpWaveHooks, assertOpWavesSpec, opWaveHooksAt, opWaveJobs, opWaveRecordPath, opWaveShare, } from "./op-waves.js";
24
- export type { OpWave, OpWaveGatePolicy, OpWaveHook, OpWaveHookFailure, OpWaveHookMoment, OpWaveHooks, OpWaveJob, OpWaveRun, OpWavesSpec } from "./op-waves.js";
25
- export { opWaveNeedsApproval, opWavePlansPath, opWaveRunsPath, parseOpWavesSpec, readOpWavePolicy, runOpWave } from "./op-waves-run.js";
26
- export type { OpWaveBaseRules, OpWaveDecision, OpWaveHookOutcome, OpWaveMember, OpWavePlanFile, OpWavePlansFile, OpWaveRunsFile, RunOpWaveOptions, RunOpWaveResult, } from "./op-waves-run.js";
23
+ export { OP_WAVE_APPLY_HOOKS_RUNNERS, OP_WAVE_DEFAULT_APPLY, OP_WAVE_GATE_POLICIES, OP_WAVE_HOOK_MOMENTS, assertOpWaveHooks, assertOpWavesSpec, opWaveApplyHooksRunBy, opWaveHooksAt, opWaveJobs, opWaveRecordPath, opWaveShare, } from "./op-waves.js";
24
+ export type { OpWave, OpWaveApplyHooksRunner, OpWaveGatePolicy, OpWaveHook, OpWaveHookFailure, OpWaveHookMoment, OpWaveHooks, OpWaveJob, OpWaveRun, OpWavesSpec } from "./op-waves.js";
25
+ export { opWaveHooksAtBase, opWaveNeedsApproval, opWavePlansPath, opWaveRunsPath, parseOpWavesSpec, readOpWaveHooksFile, readOpWavePolicy, runOpWave, runOpWaveApplyHooks, } from "./op-waves-run.js";
26
+ export type { OpWaveBaseRules, OpWaveDecision, OpWaveApplyHooksRun, OpWaveHookOutcome, OpWaveHookRun, OpWaveHooksFile, OpWaveHooksResult, OpWaveMember, OpWavePlanFile, OpWavePlansFile, OpWaveRunsFile, RunOpWaveOptions, RunOpWaveResult, } from "./op-waves-run.js";
27
27
  export { notifyOpWave, opWaveNotifyFields } from "./op-waves-notify.js";
28
28
  export type { OpWaveOutcome } from "./op-waves-notify.js";
29
29
  export { loadActivities, loadProfiles, resolveActivity } from "./activity-registry.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/op/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,YAAY,EAAE,YAAY,EAClH,cAAc,EAAE,iBAAiB,EAAE,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,aAAa,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,EACzP,UAAU,EAAE,YAAY,EAAE,OAAO,EAAE,SAAS,EAC5C,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,SAAS,EAC3D,aAAa,EAAE,aAAa,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EACrG,aAAa,EAAE,YAAY,EAC3B,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,aAAa,EAAE,eAAe,EAAE,aAAa,EACzF,eAAe,EAAE,cAAc,EAAE,aAAa,EAAE,YAAY,EAC5D,wBAAwB,EAAE,mBAAmB,EAC7C,gBAAgB,EAAE,iBAAiB,EAAE,iBAAiB,EACtD,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,WAAW,EAAE,eAAe,EAAE,cAAc,EAAE,aAAa,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AACnL,YAAY,EAAE,YAAY,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,cAAc,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACzK,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACpH,YAAY,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,eAAe,EAAE,cAAc,EAAE,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AACxJ,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AACnF,OAAO,EACL,YAAY,EAAE,eAAe,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,UAAU,EAAE,kBAAkB,GAC3H,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,eAAe,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACnH,OAAO,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAC/F,OAAO,EACL,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,EACzC,eAAe,EAAE,eAAe,EAAE,gBAAgB,EAAE,iBAAiB,EACrE,WAAW,GACZ,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,aAAa,EAAE,gBAAgB,EAC/B,iBAAiB,EAAE,oBAAoB,EACvC,aAAa,EAAE,gBAAgB,EAC/B,gBAAgB,EAAE,mBAAmB,EAAE,YAAY,EACnD,qBAAqB,EAAE,wBAAwB,EAC/C,qBAAqB,EAAE,wBAAwB,EAC/C,sBAAsB,EAAE,yBAAyB,EACjD,iBAAiB,EAAE,oBAAoB,GACxC,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,eAAe,EAAE,eAAe,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,cAAc,GACxF,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AACxF,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACvE,YAAY,EACV,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,sBAAsB,EAC5F,eAAe,EAAE,iBAAiB,EAAE,gBAAgB,EACpD,oBAAoB,EAAE,sBAAsB,EAAE,mBAAmB,GAClE,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAClE,OAAO,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACpG,YAAY,EAAE,yBAAyB,EAAE,6BAA6B,EAAE,MAAM,qBAAqB,CAAC;AACpG,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,oBAAoB,EACpB,iBAAiB,EACjB,iBAAiB,EACjB,aAAa,EACb,UAAU,EACV,gBAAgB,EAChB,WAAW,GACZ,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,MAAM,EAAE,gBAAgB,EAAE,UAAU,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC5J,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,cAAc,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AACrI,YAAY,EACV,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,cAAc,EACd,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,eAAe,GAChB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACrE,YAAY,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACpF,YAAY,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAC7F,YAAY,EAAE,kCAAkC,EAAE,MAAM,8BAA8B,CAAC;AACvF,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAClG,YAAY,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAChF,OAAO,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACpF,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAC7E,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAC9E,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAC/D,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,cAAc,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACnI,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,cAAc,EAAE,eAAe,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAC5I,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAC1E,OAAO,EACL,oBAAoB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,eAAe,EAAE,uBAAuB,EAAE,mBAAmB,GAC7I,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,kBAAkB,EACpG,mBAAmB,EAAE,aAAa,EAAE,iBAAiB,EAAE,UAAU,EAAE,oBAAoB,GACxF,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,eAAe,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,uBAAuB,GAC5G,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EACL,iBAAiB,EAAE,YAAY,EAAE,kBAAkB,EAAE,cAAc,EAAE,qBAAqB,EAAE,kBAAkB,GAC/G,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,qBAAqB,EAAE,2BAA2B,EAAE,MAAM,aAAa,CAAC;AAC3F,YAAY,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,eAAe,EAAE,WAAW,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACxI,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAC9E,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAC5H,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,YAAY,EACV,iBAAiB,EAAE,WAAW,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,UAAU,EAAE,WAAW,EAChG,WAAW,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,eAAe,GACjE,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AACzF,YAAY,EACV,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,gBAAgB,EAAE,YAAY,EAAE,cAAc,EAAE,oBAAoB,GAChG,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACzD,OAAO,EACL,gBAAgB,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,uBAAuB,EAC9G,kBAAkB,GACnB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACnF,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,0BAA0B,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACzJ,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACrE,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,EACL,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EACtD,iBAAiB,EAAE,qBAAqB,EAAE,wBAAwB,EAClE,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,gBAAgB,EACtB,sBAAsB,GACvB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,iBAAiB,EAAE,iBAAiB,EACpC,wBAAwB,EAAE,wBAAwB,EAAE,cAAc,EAAE,cAAc,EAAE,gBAAgB,EACpG,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,GAClD,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,mBAAmB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,eAAe,EAC1E,gBAAgB,EAAE,4BAA4B,EAAE,mBAAmB,EACnE,kBAAkB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,cAAc,GAC/F,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EACL,cAAc,EAAE,uBAAuB,EAAE,eAAe,EAAE,QAAQ,EAAE,wBAAwB,GAC7F,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACvI,OAAO,EACL,cAAc,EAAE,oBAAoB,EAAE,cAAc,EAAE,eAAe,EAAE,oBAAoB,EAAE,gBAAgB,EAC7G,oBAAoB,EAAE,eAAe,EAAE,gBAAgB,EAAE,cAAc,EAAE,aAAa,EACtF,YAAY,EAAE,aAAa,EAAE,oBAAoB,EAAE,mBAAmB,GACvE,MAAM,WAAW,CAAC;AACnB,YAAY,EACV,kBAAkB,EAAE,wBAAwB,EAAE,WAAW,EAAE,eAAe,EAAE,cAAc,EAC1F,aAAa,EAAE,kBAAkB,GAClC,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EACL,aAAa,EAAE,gBAAgB,EAAE,WAAW,EAAE,SAAS,EAAE,4BAA4B,GACtF,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAC7F,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EACL,kBAAkB,EAAE,gBAAgB,EAAE,cAAc,EAAE,gBAAgB,EAAE,WAAW,GACpF,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC9F,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AACnG,YAAY,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAC5E,YAAY,EACV,iBAAiB,EAAE,oBAAoB,EAAE,mBAAmB,EAC5D,gBAAgB,EAAE,mBAAmB,GACtC,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAClF,YAAY,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7F,OAAO,EAAE,mBAAmB,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC/D,YAAY,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAG9C,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAIrE,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACrH,YAAY,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/op/index.ts"],"names":[],"mappings":"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"}
@@ -29,12 +29,20 @@
29
29
  * approved set (#3762). A share job (`--share i`) re-plans its
30
30
  * slice first and refuses, without applying it, a run whose plan digest is
31
31
  * not the one the deciding job recorded (exit 4).
32
+ * - Hooks file (#3804): every apply call gets `CHANT_HOOKS`, a JSON file
33
+ * ({@link OpWaveHooksFile}) with the outcomes of the hooks that already ran
34
+ * and whether an approval stands. Under `hooks.apply.runBy: "apply"` (read
35
+ * at base) chant does not run the apply-stage hooks itself: the file hands
36
+ * them to the apply, which runs them under its own lock with
37
+ * {@link runOpWaveApplyHooks}, and their outcomes come back through
38
+ * `CHANT_HOOKS_RESULT`. A non-wave apply reads the same hooks at base with
39
+ * {@link opWaveHooksAtBase}.
32
40
  */
33
41
  import { type WaveMember } from "../gated-waves.js";
34
42
  import { type GateLedgerPort } from "./gate.js";
35
43
  import type { HeadPlans, ReviewSource } from "./gate-review.js";
36
44
  import type { TrustPolicy } from "../workspace/trust/policy.js";
37
- import { type GateApprovalSource, type OpWaveGatePolicy, type OpWaveHook, type OpWaveHookMoment, type OpWavesSpec } from "./op-waves.js";
45
+ import { type GateApprovalSource, type OpWaveApplyHooksRunner, type OpWaveGatePolicy, type OpWaveHook, type OpWaveHookMoment, type OpWavesSpec } from "./op-waves.js";
38
46
  /** What a run's plan command writes to `{plan}`. */
39
47
  export interface OpWavePlanFile {
40
48
  planDigest: string;
@@ -119,6 +127,10 @@ export interface OpWaveDecision {
119
127
  approvedAt?: string;
120
128
  /** When the wave recorded that it applies under the approval, which spent it. */
121
129
  spentAt?: string;
130
+ /** `apply`: the apply command runs the apply-stage hooks under its own lock (#3804), as read at base. Absent: chant runs them. */
131
+ applyHooksRunBy?: "apply";
132
+ /** Every hook's outcome so far in the job that wrote the record (#3804). */
133
+ outcomes?: OpWaveHookOutcome[];
122
134
  }
123
135
  /** One hook's run (#3762). */
124
136
  export interface OpWaveHookOutcome {
@@ -127,7 +139,76 @@ export interface OpWaveHookOutcome {
127
139
  /** The run, for a `perRun` hook. */
128
140
  target?: string;
129
141
  exit: number;
130
- outcome: "passed" | "failed" | "held";
142
+ outcome: "passed" | "failed" | "held" | "skipped";
143
+ /** Why the moment was skipped, for `skipped` (#3811). */
144
+ reason?: string;
145
+ }
146
+ /** One run as a hook sees it (#3804). */
147
+ export interface OpWaveHookRun {
148
+ target: string;
149
+ op: string;
150
+ /** The run's plan file, once planned. */
151
+ plan?: string;
152
+ }
153
+ /**
154
+ * What chant hands an apply command in `CHANT_HOOKS` (#3804), and what
155
+ * {@link opWaveHooksAtBase} builds for an apply outside the waves. Paths are
156
+ * relative to the job's working directory.
157
+ */
158
+ export interface OpWaveHooksFile {
159
+ version: 1;
160
+ /** The Op waves spec's name. */
161
+ opWaves: string;
162
+ /** The spec's `op`: a hook's `{op}` when it runs once for the wave. */
163
+ op: string;
164
+ wave: string;
165
+ /** 1-based; 0 when the working tree's spec has no such wave. */
166
+ number: number;
167
+ /** The commit the hooks were read at. */
168
+ base?: string;
169
+ /** The wave's set digest, once decided. */
170
+ digest?: string;
171
+ /** An approval of the digest stands, so a held hook does not stop the apply. */
172
+ approved: boolean;
173
+ /** The labels of the hooks that held the wave, approved or not. */
174
+ heldBy?: string[];
175
+ /** Who runs the apply-stage hooks. With `chant`, `hooks` is empty: chant runs them around the apply. */
176
+ runBy: OpWaveApplyHooksRunner;
177
+ /** The runs this apply call applies. */
178
+ runs: OpWaveHookRun[];
179
+ /** The runs file, a hook's `{runs}` and `CHANT_RUNS`. */
180
+ runsFile: string;
181
+ /** The hooks the apply runs, by moment, as read at base. */
182
+ hooks: Partial<Record<OpWaveHookMoment, OpWaveHook[]>>;
183
+ /** The hooks that already ran for this wave in this job, and how each came out. */
184
+ outcomes: OpWaveHookOutcome[];
185
+ /** Where {@link runOpWaveApplyHooks} writes its outcomes (`CHANT_HOOKS_RESULT`), which chant reads back. */
186
+ result?: string;
187
+ /** Set when the hooks at base could not be read: none run, and the runner refuses to proceed. */
188
+ refused?: string;
189
+ }
190
+ /** What {@link runOpWaveApplyHooks} writes to `CHANT_HOOKS_RESULT` (#3804). */
191
+ export interface OpWaveHooksResult {
192
+ version: 1;
193
+ /** The moments the apply ran, empty ones included. */
194
+ moments: OpWaveHookMoment[];
195
+ outcomes: OpWaveHookOutcome[];
196
+ }
197
+ /** One call of {@link runOpWaveApplyHooks}. */
198
+ export interface OpWaveApplyHooksRun {
199
+ moment: OpWaveHookMoment;
200
+ outcomes: OpWaveHookOutcome[];
201
+ /** Set when a hook with `onFailure: fail` failed; the hooks after it did not run. */
202
+ failed?: string;
203
+ /** The `onFailure: approve` hooks that failed, or why the hooks could not be read at base. */
204
+ held: string[];
205
+ /**
206
+ * Whether the apply may go on: no hook failed, and nothing held it or an
207
+ * approval of the digest stands. When false after a hold, the apply applies
208
+ * nothing and exits non-zero; chant then waits for, or finds, an approval
209
+ * and calls it again.
210
+ */
211
+ proceed: boolean;
131
212
  }
132
213
  /** Runs one argv; returns its exit code. `env` is added to the job's environment. Injected so tests run no processes. */
133
214
  export type OpWaveExec = (argv: string[], cwd: string, env?: Record<string, string>) => number;
@@ -150,6 +231,8 @@ export interface OpWaveBaseRules {
150
231
  hooksRefused?: string;
151
232
  /** The spec at base sets `approvals.spend` (#3784). */
152
233
  spend: boolean;
234
+ /** Who runs the apply-stage hooks, as read at base (#3804). `chant` when the file, the wave or the hooks are missing there. */
235
+ applyHooksRunBy: OpWaveApplyHooksRunner;
153
236
  }
154
237
  /**
155
238
  * The policy of wave `name`, as the spec file at `base` declares it. The
@@ -211,6 +294,55 @@ export interface RunOpWaveResult {
211
294
  /** The hooks that ran (#3762). */
212
295
  hooks: OpWaveHookOutcome[];
213
296
  }
297
+ /**
298
+ * Read the hooks file chant hands an apply command (#3804): the one at
299
+ * `path`, by default `CHANT_HOOKS`. Null when there is none, as for an apply
300
+ * that a wave job did not start.
301
+ */
302
+ export declare function readOpWaveHooksFile(path?: string | undefined, cwd?: string): OpWaveHooksFile | null;
303
+ /**
304
+ * Run one moment's hooks from a hooks file (#3804): what an apply command
305
+ * calls under its own lock, `before-apply` right before it changes anything
306
+ * and `after-apply` once it has, when the spec's `hooks.apply.runBy` at base
307
+ * is `apply`. Also what a product's apply outside the waves calls, with a
308
+ * file from {@link opWaveHooksAtBase}. A moment the file has no hooks for
309
+ * runs nothing and proceeds, so an apply can call it whoever runs the hooks.
310
+ *
311
+ * Each call adds its outcomes to the file's `result` (`CHANT_HOOKS_RESULT`),
312
+ * which chant reads back after the apply exits: the outcomes join the wave's
313
+ * result and decision record, and a hold makes the wave wait for, or find,
314
+ * an approval of its digest. `skip: "<reason>"` runs nothing and records the
315
+ * moment as skipped with the reason (#3811), for an apply that changes nothing:
316
+ * chant counts the moment as handled. A skip without a reason throws. `exec` runs one hook; inject it to run a hook
317
+ * in process or to keep its output.
318
+ */
319
+ export declare function runOpWaveApplyHooks(file: OpWaveHooksFile, moment: OpWaveHookMoment, options?: {
320
+ cwd?: string;
321
+ exec?: OpWaveExec;
322
+ log?: (line: string) => void;
323
+ skip?: string;
324
+ }): OpWaveApplyHooksRun;
325
+ /**
326
+ * The hooks of wave `wave` read at base, for an apply outside the waves
327
+ * (#3804): a product's own `apply <env>` that runs the same Op as the wave
328
+ * does, and the same hooks. All four moments are in the file; the apply runs
329
+ * the ones it has a stage for with {@link runOpWaveApplyHooks}. Nothing is
330
+ * approved: a held hook stops the apply. The wave or the file missing at
331
+ * base has no hooks, as in a wave job.
332
+ */
333
+ export declare function opWaveHooksAtBase(options: {
334
+ specFile: string;
335
+ wave: string;
336
+ /** The runs this apply applies. Default: the wave's runs in the working tree's spec. */
337
+ runs?: ReadonlyArray<{
338
+ target: string;
339
+ op?: string;
340
+ }>;
341
+ /** Default: the spec's `base`, else `HEAD^1`. */
342
+ base?: string;
343
+ cwd?: string;
344
+ show?: OpWaveShowAtBase;
345
+ }): OpWaveHooksFile;
214
346
  /** Whether `policy` lets a wave with these members through without an approval. */
215
347
  export declare function opWaveNeedsApproval(policy: OpWaveGatePolicy, members: readonly OpWaveMember[]): boolean;
216
348
  /** Plan, decide and apply one wave, or one part of a wide wave. */