@coreplane/switchboard 1.239.0 → 1.241.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/config/config.example.yaml +32 -7
- package/dist/assets/deploy/cloudflare/worker.ts +77 -18
- package/dist/assets/deploy/cloudflare-memory/worker.ts +115 -1
- package/dist/assets/deploy/cloudflare-memory/wrangler.template.jsonc +7 -1
- package/dist/assets/deploy/cloudflare-resident/worker.ts +224 -70
- package/dist/assets/deploy/secrets.manifest.json +7 -1
- package/dist/assets/package-lock.json +207 -3
- package/dist/assets/package.json +2 -1
- package/dist/assets/source.json +3 -3
- package/dist/assets/src/agents/registry.ts +27 -8
- package/dist/assets/src/core/authz/actor.ts +38 -2
- package/dist/assets/src/core/authz/grants.ts +20 -9
- package/dist/assets/src/core/authz/policy.ts +6 -0
- package/dist/assets/src/core/coordinator/contract.ts +49 -1
- package/dist/assets/src/core/coordinator/driver.ts +30 -1
- package/dist/assets/src/core/coordinator/instancesRoute.ts +72 -0
- package/dist/assets/src/core/costs.ts +1119 -0
- package/dist/assets/src/core/costsSnapshotStore.ts +209 -0
- package/dist/assets/src/core/delivery.ts +4 -11
- package/dist/assets/src/core/deliverySnapshotStore.ts +12 -28
- package/dist/assets/src/core/ingressTokens.ts +27 -13
- package/dist/assets/src/core/runLedger/types.ts +3 -0
- package/dist/assets/src/core/runRecord.ts +10 -3
- package/dist/assets/src/core/schedules.ts +2 -1
- package/dist/assets/src/core/ship/coordinator.ts +92 -7
- package/dist/assets/src/core/snapshotAge.ts +30 -0
- package/dist/assets/src/core/stateWorkerRef.ts +41 -0
- package/dist/assets/src/core/trace/attrs.ts +1 -1
- package/dist/assets/src/execution/bindingPurge.ts +2 -2
- package/dist/assets/src/execution/residentAutoRebuild.ts +73 -0
- package/dist/assets/src/execution/residentRefresh.ts +25 -3
- package/dist/assets/src/execution/restoreWaiters.ts +61 -0
- package/dist/assets/src/mcp/registry.ts +7 -0
- package/dist/assets/web/dist/.vite/manifest.json +509 -427
- package/dist/assets/web/dist/assets/AppShell-DEy5jRuS.js +1 -0
- package/dist/assets/web/dist/assets/CostsPage-BuHD-Bv7.js +2 -0
- package/dist/assets/web/dist/assets/DeliveryPage-BGpPr5xr.js +1 -0
- package/dist/assets/web/dist/assets/HomePage-dKBvWh-E.js +2 -0
- package/dist/assets/web/dist/assets/NotFoundPage-DtmQM2gD.js +1 -0
- package/dist/assets/web/dist/assets/PendingTurnRow-CrEAWx5b.js +1 -0
- package/dist/assets/web/dist/assets/{ResidentDetailPage-CrfCd8fx.js → ResidentDetailPage-C-48fxat.js} +1 -1
- package/dist/assets/web/dist/assets/ResidentsIndexPage-CBBIjPOa.js +1 -0
- package/dist/assets/web/dist/assets/RunFoldRow-B6vxjcBI.js +1 -0
- package/dist/assets/web/dist/assets/RunRoutePage-CQjd3Cz-.js +6 -0
- package/dist/assets/web/dist/assets/RunsIndexPage-Cy9G6D0p.js +1 -0
- package/dist/assets/web/dist/assets/{RunsTabs-XpCqeWu4.js → RunsTabs-CtNGNnhN.js} +1 -1
- package/dist/assets/web/dist/assets/ScheduledPage-Ci1Ygqnc.js +1 -0
- package/dist/assets/web/dist/assets/SettingsPage-DxmJvAmT.js +1 -0
- package/dist/assets/web/dist/assets/SlackMark-VDNs7Vjh.js +1 -0
- package/dist/assets/web/dist/assets/{StatusDot-DkXALvKf.js → StatusDot-DPWE1JmO.js} +1 -1
- package/dist/assets/web/dist/assets/{Tooltip-DtLIyi5P.js → Tooltip-DKhSRH9t.js} +1 -1
- package/dist/assets/web/dist/assets/UnitRoutePage-DEN5bSQf.js +1 -0
- package/dist/assets/web/dist/assets/{angular-html-DYtV9uIN.js → angular-html-DgSK1qvr.js} +1 -1
- package/dist/assets/web/dist/assets/{angular-ts-DF2P25Zp.js → angular-ts-D3gNdiSG.js} +1 -1
- package/dist/assets/web/dist/assets/{apl-Ff96wxko.js → apl-CkHCYM8I.js} +1 -1
- package/dist/assets/web/dist/assets/{astro-LbBnZna-.js → astro-DLm45axt.js} +1 -1
- package/dist/assets/web/dist/assets/{blade-DltuxeBW.js → blade-BKa-VE-c.js} +1 -1
- package/dist/assets/web/dist/assets/{c-DBg7yuPx.js → c-C8NCNWai.js} +1 -1
- package/dist/assets/web/dist/assets/{chapel-C83hUAds.js → chapel-DqDQ7IKH.js} +1 -1
- package/dist/assets/web/dist/assets/{cobol-DB0yV_2D.js → cobol-j4vCD6AJ.js} +1 -1
- package/dist/assets/web/dist/assets/{coffee-Bpih6Kzl.js → coffee-C6oDVH-0.js} +1 -1
- package/dist/assets/web/dist/assets/{cpp-CU00ZeRn.js → cpp-Bd3A1Rtc.js} +1 -1
- package/dist/assets/web/dist/assets/{crystal-f8ImJkux.js → crystal-CUOgOw0d.js} +1 -1
- package/dist/assets/web/dist/assets/{css-4VrC4ISo.js → css-RYljyv7G.js} +1 -1
- package/dist/assets/web/dist/assets/delivery-CTdOqpVK.js +1 -0
- package/dist/assets/web/dist/assets/{dist-DtA7JiH5.js → dist-BnJXKHCY.js} +2 -2
- package/dist/assets/web/dist/assets/durationTone-DXG-3R7_.js +1 -0
- package/dist/assets/web/dist/assets/{edge-BwFDzfS_.js → edge-BJhNdQDc.js} +1 -1
- package/dist/assets/web/dist/assets/{elixir-wwIYIpJm.js → elixir-g1AWOAfT.js} +1 -1
- package/dist/assets/web/dist/assets/{elm-DyDuM3v3.js → elm-CER5e4Dz.js} +1 -1
- package/dist/assets/web/dist/assets/{erb-DFJBEVrh.js → erb-D5aOmMQf.js} +1 -1
- package/dist/assets/web/dist/assets/eventSource-tRprhqm7.js +1 -0
- package/dist/assets/web/dist/assets/favicon-D1hkyC5j.js +1 -0
- package/dist/assets/web/dist/assets/{git-rebase-CuDv-44X.js → git-rebase-D6rfV8jp.js} +1 -1
- package/dist/assets/web/dist/assets/{glimmer-js-ZFnrm4J3.js → glimmer-js-BvDH3-mC.js} +1 -1
- package/dist/assets/web/dist/assets/{glimmer-ts-XY4Yqshp.js → glimmer-ts-DChtNs9V.js} +1 -1
- package/dist/assets/web/dist/assets/{glsl-xnR8efx0.js → glsl-BHlsWrxf.js} +1 -1
- package/dist/assets/web/dist/assets/{graphql-D-gO5tnP.js → graphql-BsaBHb53.js} +1 -1
- package/dist/assets/web/dist/assets/{hack-CSxbW4ax.js → hack-0lbDosCd.js} +1 -1
- package/dist/assets/web/dist/assets/{haml-D4ar42QT.js → haml-BKtco2vr.js} +1 -1
- package/dist/assets/web/dist/assets/{handlebars-CMLbDE7o.js → handlebars-BE_cj04z.js} +1 -1
- package/dist/assets/web/dist/assets/{html-CVekU4Fs.js → html-BNcr8EVd.js} +1 -1
- package/dist/assets/web/dist/assets/{html-derivative-St3HHw_N.js → html-derivative-BsJt2Kei.js} +1 -1
- package/dist/assets/web/dist/assets/{http-Mon0DWHS.js → http-BR5P8ER_.js} +1 -1
- package/dist/assets/web/dist/assets/{hurl-Cbl5IvBs.js → hurl-CtHalpWS.js} +1 -1
- package/dist/assets/web/dist/assets/{localIso-CNA-bhS-.js → indexFormat-B-pd4r7Y.js} +1 -1
- package/dist/assets/web/dist/assets/{indexRow-FLJu5mR9.js → indexRow-BD1VT8o8.js} +1 -1
- package/dist/assets/web/dist/assets/{java-Bprck1l-.js → java-BXVdhN11.js} +1 -1
- package/dist/assets/web/dist/assets/{javascript-C73G1-UR.js → javascript-CyoShgNQ.js} +1 -1
- package/dist/assets/web/dist/assets/{jinja-C4DuRxYJ.js → jinja-BQYZzHex.js} +1 -1
- package/dist/assets/web/dist/assets/{jison-B2U03GEC.js → jison-DqrUa1Eq.js} +1 -1
- package/dist/assets/web/dist/assets/{json-CvTZUDzx.js → json-CQiTF9Jj.js} +1 -1
- package/dist/assets/web/dist/assets/{jsx-DMWKCzwo.js → jsx-Y16MkCYQ.js} +1 -1
- package/dist/assets/web/dist/assets/{julia-Czn7bV_3.js → julia-zn16YONe.js} +1 -1
- package/dist/assets/web/dist/assets/{just-BBxpNfvC.js → just-DcVeN_MN.js} +1 -1
- package/dist/assets/web/dist/assets/{latex-Bw9yg5mr.js → latex-BNNoF-WP.js} +1 -1
- package/dist/assets/web/dist/assets/{liquid-BRCjaydE.js → liquid-DAi6qHzn.js} +1 -1
- package/dist/assets/web/dist/assets/localIso-L06jV29p.js +1 -0
- package/dist/assets/web/dist/assets/{lua-DUu2KIC0.js → lua-BswS0axw.js} +1 -1
- package/dist/assets/web/dist/assets/main-B3is4sk6.js +28 -0
- package/dist/assets/web/dist/assets/main-DP_zSemY.css +1 -0
- package/dist/assets/web/dist/assets/{marko-CZJZIlSQ.js → marko-C-4hkmQZ.js} +1 -1
- package/dist/assets/web/dist/assets/{mdc-BmClq6fd.js → mdc-0ZaHUkS7.js} +1 -1
- package/dist/assets/web/dist/assets/{nginx-Bmt3xSEm.js → nginx-DBDg5tOR.js} +1 -1
- package/dist/assets/web/dist/assets/{nim-Da-i0utK.js → nim-BVMz49Yw.js} +1 -1
- package/dist/assets/web/dist/assets/{org-BvAre2qP.js → org-1hO_K6ya.js} +1 -1
- package/dist/assets/web/dist/assets/{perl-v9WwFFtB.js → perl-CVRQLvVk.js} +1 -1
- package/dist/assets/web/dist/assets/{php-DTU4Zyze.js → php-K9nCrB9n.js} +1 -1
- package/dist/assets/web/dist/assets/{pug-DhTwEqTO.js → pug-CTxOcmO3.js} +1 -1
- package/dist/assets/web/dist/assets/{qml-BUD9AC_c.js → qml-CmCkfG5q.js} +1 -1
- package/dist/assets/web/dist/assets/{r-CAnu56n7.js → r-pP47Xn1X.js} +1 -1
- package/dist/assets/web/dist/assets/{razor-Dn0P4gAq.js → razor-BpW6r3tC.js} +1 -1
- package/dist/assets/web/dist/assets/{regexp-CZyBRaVW.js → regexp-CINgdY4N.js} +1 -1
- package/dist/assets/web/dist/assets/{rst-Ckyd5UsY.js → rst-BZIUEq2Q.js} +1 -1
- package/dist/assets/web/dist/assets/{ruby-BBQ62fsx.js → ruby-nzAOxz6r.js} +1 -1
- package/dist/assets/web/dist/assets/{sas-CW35hMxG.js → sas-QIS1bFth.js} +1 -1
- package/dist/assets/web/dist/assets/{scss-CpV8pF4J.js → scss-BuhBBVNt.js} +1 -1
- package/dist/assets/web/dist/assets/settingsApi-priOPiwu.js +1 -0
- package/dist/assets/web/dist/assets/{shellscript-CRJvsmE1.js → shellscript-CQc1vXbk.js} +1 -1
- package/dist/assets/web/dist/assets/{shellsession-CyriUZxf.js → shellsession-CoubCAUv.js} +1 -1
- package/dist/assets/web/dist/assets/{soy-D5QzYUeg.js → soy-CPRzlder.js} +1 -1
- package/dist/assets/web/dist/assets/{sql-CUBf_Mse.js → sql-W9krb8-9.js} +1 -1
- package/dist/assets/web/dist/assets/sseReplay-yji8a1wM.js +9 -0
- package/dist/assets/web/dist/assets/{stata-B-4n4ITz.js → stata-CuISJEC0.js} +1 -1
- package/dist/assets/web/dist/assets/{surrealql-RuWZBLtr.js → surrealql-CSet7584.js} +1 -1
- package/dist/assets/web/dist/assets/{svelte-CO5YzCsK.js → svelte-ChXTSYwK.js} +1 -1
- package/dist/assets/web/dist/assets/{templ-DbkWVaBF.js → templ-Bm55v62k.js} +1 -1
- package/dist/assets/web/dist/assets/{tex-IuxoEcId.js → tex-CICMX8Gj.js} +1 -1
- package/dist/assets/web/dist/assets/{ts-tags-g6dGQPjd.js → ts-tags-CA1UzWyB.js} +1 -1
- package/dist/assets/web/dist/assets/{tsx-WnXwiyH2.js → tsx-Dy04HNbv.js} +1 -1
- package/dist/assets/web/dist/assets/{twig-Lx1G7PnY.js → twig-BBHsVnVD.js} +1 -1
- package/dist/assets/web/dist/assets/{typescript-BQd7biLF.js → typescript-Dvc-wVBT.js} +1 -1
- package/dist/assets/web/dist/assets/{typst-D2lXPHK7.js → typst-Dvlqx3_q.js} +1 -1
- package/dist/assets/web/dist/assets/{vue-html-DZPaRte4.js → vue-html-D4YxT3An.js} +1 -1
- package/dist/assets/web/dist/assets/{vue-vine-DbmZCmu-.js → vue-vine-0YE7uQlH.js} +1 -1
- package/dist/assets/web/dist/assets/{vue-4wVQD9y9.js → vue-zWi1MNU2.js} +1 -1
- package/dist/assets/web/dist/assets/{xml-DV16szyo.js → xml-gm-iksZB.js} +1 -1
- package/dist/assets/web/dist/assets/{xsl-eo5-1sk0.js → xsl-D_W5BcrY.js} +1 -1
- package/dist/assets/web/dist/assets/{yaml-BXdX8HZf.js → yaml-CHmZ21wZ.js} +1 -1
- package/dist/cli.js +6014 -2362
- package/package.json +1 -1
- package/dist/assets/web/dist/assets/AppShell-MYHNxJks.js +0 -1
- package/dist/assets/web/dist/assets/CostsPage-DKk9OjB0.js +0 -2
- package/dist/assets/web/dist/assets/DeliveryPage-Kz6js49i.js +0 -1
- package/dist/assets/web/dist/assets/NotFoundPage-BXLrQQzy.js +0 -1
- package/dist/assets/web/dist/assets/ResidentsIndexPage-BdW0k2h3.js +0 -1
- package/dist/assets/web/dist/assets/RunFoldRow-CbTd9SiS.js +0 -9
- package/dist/assets/web/dist/assets/RunRoutePage-br0H-8Pz.js +0 -6
- package/dist/assets/web/dist/assets/RunsIndexPage-Bnzb15zH.js +0 -1
- package/dist/assets/web/dist/assets/ScheduledPage-NLhjzHAz.js +0 -1
- package/dist/assets/web/dist/assets/SettingsPage-D-i0uVw-.js +0 -1
- package/dist/assets/web/dist/assets/UnitRoutePage-DGXukEAH.js +0 -1
- package/dist/assets/web/dist/assets/durationTone-CYMux25J.js +0 -1
- package/dist/assets/web/dist/assets/favicon-CSt-qDvc.js +0 -1
- package/dist/assets/web/dist/assets/main-CFHF2RvQ.css +0 -1
- package/dist/assets/web/dist/assets/main-Dht0in4v.js +0 -28
|
@@ -46,6 +46,9 @@ import {
|
|
|
46
46
|
type ChildFacts,
|
|
47
47
|
type MergeReadyFacts,
|
|
48
48
|
type CoordinatorAction,
|
|
49
|
+
isAddressSeverity,
|
|
50
|
+
type AddressSeverity,
|
|
51
|
+
type AddressSeveritySource,
|
|
49
52
|
type PlanGraph,
|
|
50
53
|
type PlanUnitNode,
|
|
51
54
|
type ShipCaps,
|
|
@@ -54,7 +57,7 @@ import {
|
|
|
54
57
|
type UnitPipelineInput,
|
|
55
58
|
type UnitPipelineState,
|
|
56
59
|
} from "../ship/coordinator.js";
|
|
57
|
-
import { isCoordinatorUnit, runFinishedEventType, type CoordinatorUnit } from "./contract.js";
|
|
60
|
+
import { checksSettledEventType, isCoordinatorUnit, runFinishedEventType, type CoordinatorUnit } from "./contract.js";
|
|
58
61
|
|
|
59
62
|
const MIN = 60_000;
|
|
60
63
|
|
|
@@ -159,6 +162,9 @@ interface PlanFacts {
|
|
|
159
162
|
planId?: string;
|
|
160
163
|
/** Who merges, as the instance's field has it: the plan route answers it, `person` when absent. */
|
|
161
164
|
merge: "runner" | "person";
|
|
165
|
+
/** The severity to address, beside `merge`: the level an approve's findings are held to, and which layer set it. */
|
|
166
|
+
addressSeverity: AddressSeverity;
|
|
167
|
+
addressSeveritySource: AddressSeveritySource;
|
|
162
168
|
/** The instance's mark as the plan route answers it: a generated one-unit plan (a `plan` with no `path`). */
|
|
163
169
|
generated: boolean;
|
|
164
170
|
repo: string;
|
|
@@ -189,6 +195,11 @@ function readPlan(a: BotAnswer): PlanFacts {
|
|
|
189
195
|
return {
|
|
190
196
|
...(typeof b.planId === "string" ? { planId: b.planId } : {}),
|
|
191
197
|
merge: b.merge === "runner" ? "runner" : "person",
|
|
198
|
+
addressSeverity: isAddressSeverity(b.addressSeverity) ? b.addressSeverity : "minor",
|
|
199
|
+
addressSeveritySource:
|
|
200
|
+
b.addressSeveritySource === "run" || b.addressSeveritySource === "user" || b.addressSeveritySource === "channel"
|
|
201
|
+
? b.addressSeveritySource
|
|
202
|
+
: "org",
|
|
192
203
|
generated: b.generated === true,
|
|
193
204
|
repo: b.repo,
|
|
194
205
|
base: b.base,
|
|
@@ -418,6 +429,22 @@ async function perform(
|
|
|
418
429
|
case "sleep":
|
|
419
430
|
await step.sleep(action.step, action.ms);
|
|
420
431
|
return { type: "sleep", step: action.step };
|
|
432
|
+
case "wait-checks": {
|
|
433
|
+
// The intake's checks-settled event at the approved head (http-ingress.md
|
|
434
|
+
// item 12), with the machine's bounded timeout as the fallback: either
|
|
435
|
+
// way the machine re-asks the merge door, which is the guard.
|
|
436
|
+
let outcome: "event" | "timeout";
|
|
437
|
+
try {
|
|
438
|
+
await step.waitForEvent(action.step, {
|
|
439
|
+
type: checksSettledEventType(action.headSha),
|
|
440
|
+
timeout: action.timeoutMs,
|
|
441
|
+
});
|
|
442
|
+
outcome = "event";
|
|
443
|
+
} catch {
|
|
444
|
+
outcome = "timeout";
|
|
445
|
+
}
|
|
446
|
+
return { type: "wait-checks", step: action.step, outcome };
|
|
447
|
+
}
|
|
421
448
|
case "merge":
|
|
422
449
|
return mergeReturn(
|
|
423
450
|
action.step,
|
|
@@ -465,6 +492,8 @@ async function runUnit(
|
|
|
465
492
|
// seeded plan and `person` on a task, and the door re-checks it — the
|
|
466
493
|
// branch's name never decides.
|
|
467
494
|
merge: plan.merge,
|
|
495
|
+
addressSeverity: plan.addressSeverity,
|
|
496
|
+
addressSeveritySource: plan.addressSeveritySource,
|
|
468
497
|
generated: plan.generated,
|
|
469
498
|
...(resume !== undefined ? { resume } : {}),
|
|
470
499
|
...(lastPush !== undefined ? { lastPush } : {}),
|
|
@@ -131,6 +131,78 @@ export function parseInstanceStatusPath(pathname: string): string | undefined {
|
|
|
131
131
|
return INSTANCE_ID_PATTERN.test(id) ? id : undefined;
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
+
/** `POST /admin/coordinator/instances/<id>/events` — the shim's event relay
|
|
135
|
+
* (docs/reference/specs/http-ingress.md item 12): the bot holds no Workflow
|
|
136
|
+
* binding, so the check-run intake sends `checks-settled-<head>` through this
|
|
137
|
+
* route and the shim's own `SHIP_COORDINATOR.get(id).sendEvent` delivers it. */
|
|
138
|
+
export const COORDINATOR_INSTANCE_EVENTS_SUFFIX = "/events";
|
|
139
|
+
|
|
140
|
+
/** The instance id an event path names, or undefined for any other path. */
|
|
141
|
+
export function parseInstanceEventPath(pathname: string): string | undefined {
|
|
142
|
+
if (!pathname.startsWith(COORDINATOR_INSTANCE_STATUS_PREFIX)) return undefined;
|
|
143
|
+
if (!pathname.endsWith(COORDINATOR_INSTANCE_EVENTS_SUFFIX)) return undefined;
|
|
144
|
+
const id = pathname.slice(
|
|
145
|
+
COORDINATOR_INSTANCE_STATUS_PREFIX.length,
|
|
146
|
+
pathname.length - COORDINATOR_INSTANCE_EVENTS_SUFFIX.length,
|
|
147
|
+
);
|
|
148
|
+
return INSTANCE_ID_PATTERN.test(id) ? id : undefined;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export type ParsedSendEvent = { ok: true; type: string; payload: unknown } | { ok: false; reason: string };
|
|
152
|
+
|
|
153
|
+
/** The Workflow event alphabet — what the engine's `sendEvent` accepts as a type. */
|
|
154
|
+
const EVENT_TYPE_PATTERN = /^[A-Za-z0-9_-]{1,100}$/;
|
|
155
|
+
|
|
156
|
+
/** The body: `{ type, payload? }` — the typed event the instance's `waitForEvent` waits on. */
|
|
157
|
+
export function parseSendEventRequest(text: string): ParsedSendEvent {
|
|
158
|
+
let parsed: unknown;
|
|
159
|
+
try {
|
|
160
|
+
parsed = JSON.parse(text);
|
|
161
|
+
} catch {
|
|
162
|
+
return { ok: false, reason: "body is not valid JSON" };
|
|
163
|
+
}
|
|
164
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
|
|
165
|
+
return { ok: false, reason: "body must be a JSON object" };
|
|
166
|
+
const b = parsed as Record<string, unknown>;
|
|
167
|
+
if (typeof b.type !== "string" || !EVENT_TYPE_PATTERN.test(b.type))
|
|
168
|
+
return { ok: false, reason: "`type` must be a Workflow event type: letters, digits, `_` and `-`, at most 100" };
|
|
169
|
+
return { ok: true, type: b.type, payload: b.payload };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** How the send ended on the shim: delivered, no such instance (the engine's
|
|
173
|
+
* own `instance.not_found`), or the engine refusing by reason (an instance
|
|
174
|
+
* that already ended is a refusal, not an absence). */
|
|
175
|
+
export type SendEventOutcome =
|
|
176
|
+
{ kind: "sent"; id: string } | { kind: "absent"; id: string } | { kind: "failed"; id: string; reason: string };
|
|
177
|
+
|
|
178
|
+
export function sendEventResponse(outcome: SendEventOutcome): { status: number; body: Record<string, unknown> } {
|
|
179
|
+
switch (outcome.kind) {
|
|
180
|
+
case "sent":
|
|
181
|
+
return { status: 200, body: { ok: true, id: outcome.id, sent: true } };
|
|
182
|
+
case "absent":
|
|
183
|
+
return { status: 404, body: { ok: false, error: "no_instance", id: outcome.id } };
|
|
184
|
+
case "failed":
|
|
185
|
+
return { status: 502, body: { ok: false, error: "send_failed", id: outcome.id, message: outcome.reason } };
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** The send answer as the bot reads it back: sent, absent, or unanswered by reason. */
|
|
190
|
+
export type SendEventAnswer = { kind: "sent" } | { kind: "absent" } | { kind: "unanswered"; reason: string };
|
|
191
|
+
|
|
192
|
+
export function readSendEventAnswer(status: number, text: string): SendEventAnswer {
|
|
193
|
+
let parsed: unknown;
|
|
194
|
+
try {
|
|
195
|
+
parsed = JSON.parse(text);
|
|
196
|
+
} catch {
|
|
197
|
+
parsed = undefined;
|
|
198
|
+
}
|
|
199
|
+
const body = typeof parsed === "object" && parsed !== null ? (parsed as Record<string, unknown>) : undefined;
|
|
200
|
+
if (status === 200 && body?.ok === true && body.sent === true) return { kind: "sent" };
|
|
201
|
+
if (status === 404 && body?.error === "no_instance") return { kind: "absent" };
|
|
202
|
+
const detail = typeof body?.error === "string" ? body.error : text.slice(0, 200);
|
|
203
|
+
return { kind: "unanswered", reason: `HTTP ${status} — ${detail}` };
|
|
204
|
+
}
|
|
205
|
+
|
|
134
206
|
/** How the status read ended on the shim: the platform's status word, no such
|
|
135
207
|
* instance, or the engine failing by reason. */
|
|
136
208
|
export type InstanceStatusOutcome =
|