spur-board 0.9.0 → 0.9.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/dist/cli.mjs +3 -3
- package/dist/{client-Co0Pogjy.mjs → client-C4Ee3Gfs.mjs} +1 -1
- package/dist/client.mjs +1 -1
- package/dist/{config-DL_Tm7un.mjs → config-BmT8hPi8.mjs} +2 -2
- package/dist/daemon.mjs +140 -28
- package/dist/mcp.mjs +3 -3
- package/dist/{src-C-NNf__4.mjs → src-DyDmiehC.mjs} +10 -2
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { H as requiredSecretKeys, N as ERRORS, U as SLACK_BOT_SCOPES, V as configSchema, l as OPERATIONS, n as SYSTEMD_SERVICE_SETTINGS } from "./src-
|
|
2
|
-
import { a as errorExitCode, i as CliError, n as requestOperation, o as renderCliError, r as requestTokenEnrollment, s as escapeTerminalControls } from "./client-
|
|
3
|
-
import { a as memoryVerdictCache, c as loadEnrollmentConfig, i as fileVerdictCache, l as resolveDaemonAddress, n as cliPackageVersion, o as configFile, s as loadClientConfig, t as checkCatalog } from "./config-
|
|
1
|
+
import { H as requiredSecretKeys, N as ERRORS, U as SLACK_BOT_SCOPES, V as configSchema, l as OPERATIONS, n as SYSTEMD_SERVICE_SETTINGS } from "./src-DyDmiehC.mjs";
|
|
2
|
+
import { a as errorExitCode, i as CliError, n as requestOperation, o as renderCliError, r as requestTokenEnrollment, s as escapeTerminalControls } from "./client-C4Ee3Gfs.mjs";
|
|
3
|
+
import { a as memoryVerdictCache, c as loadEnrollmentConfig, i as fileVerdictCache, l as resolveDaemonAddress, n as cliPackageVersion, o as configFile, s as loadClientConfig, t as checkCatalog } from "./config-BmT8hPi8.mjs";
|
|
4
4
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
5
5
|
import { chmodSync, closeSync, constants, existsSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, readlinkSync, realpathSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
6
6
|
import { homedir, hostname, userInfo } from "node:os";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { F as errorEnvelopeSchema, I as successEnvelopeSchema, N as ERRORS, P as errorContextSchema, d as renderJsonError } from "./src-
|
|
1
|
+
import { F as errorEnvelopeSchema, I as successEnvelopeSchema, N as ERRORS, P as errorContextSchema, d as renderJsonError } from "./src-DyDmiehC.mjs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { request } from "node:http";
|
package/dist/client.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as requestOperation, r as requestTokenEnrollment, t as fetchCatalogVersion } from "./client-
|
|
1
|
+
import { n as requestOperation, r as requestTokenEnrollment, t as fetchCatalogVersion } from "./client-C4Ee3Gfs.mjs";
|
|
2
2
|
export { fetchCatalogVersion, requestOperation, requestTokenEnrollment };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { V as configSchema, l as OPERATIONS, s as buildCatalogSnapshot } from "./src-
|
|
2
|
-
import { i as CliError, t as fetchCatalogVersion } from "./client-
|
|
1
|
+
import { V as configSchema, l as OPERATIONS, s as buildCatalogSnapshot } from "./src-DyDmiehC.mjs";
|
|
2
|
+
import { i as CliError, t as fetchCatalogVersion } from "./client-C4Ee3Gfs.mjs";
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
4
|
import { closeSync, constants, fstatSync, lstatSync, mkdirSync, openSync, readFileSync, realpathSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { homedir } from "node:os";
|
package/dist/daemon.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as parseActorId, B as RESERVED_PROJECT, C as prFactsSchema, D as actorIdSchema, E as ticketFactsSchema, H as requiredSecretKeys, I as successEnvelopeSchema, M as taskKeySchema, N as ERRORS, O as encodeActorId, S as SESSION_STATUSES, T as sessionFactsSchema, U as SLACK_BOT_SCOPES, V as configSchema, _ as messageRefSchema, a as entityRefSchema, b as eventPayloadSchemas, c as roleCanUseOperation, g as interactionRefSchema, h as inboundEventSchema, i as batchSchema, j as parseRef$1, k as errorCodeOf, l as OPERATIONS, m as conversationRefSchema, o as sourceCursorSchema, p as SPACE_IDS, r as AdapterError, s as buildCatalogSnapshot, t as BOARD_TICK_MS, u as renderHttpProblem, v as userIdSchema, w as prRefSchema, x as SESSION_HEALTH_STATES, y as EVENT_KINDS, z as DEFAULT_SLACK_COMMAND } from "./src-
|
|
1
|
+
import { A as parseActorId, B as RESERVED_PROJECT, C as prFactsSchema, D as actorIdSchema, E as ticketFactsSchema, H as requiredSecretKeys, I as successEnvelopeSchema, M as taskKeySchema, N as ERRORS, O as encodeActorId, S as SESSION_STATUSES, T as sessionFactsSchema, U as SLACK_BOT_SCOPES, V as configSchema, _ as messageRefSchema, a as entityRefSchema, b as eventPayloadSchemas, c as roleCanUseOperation, g as interactionRefSchema, h as inboundEventSchema, i as batchSchema, j as parseRef$1, k as errorCodeOf, l as OPERATIONS, m as conversationRefSchema, o as sourceCursorSchema, p as SPACE_IDS, r as AdapterError, s as buildCatalogSnapshot, t as BOARD_TICK_MS, u as renderHttpProblem, v as userIdSchema, w as prRefSchema, x as SESSION_HEALTH_STATES, y as EVENT_KINDS, z as DEFAULT_SLACK_COMMAND } from "./src-DyDmiehC.mjs";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import { createHash, randomUUID } from "node:crypto";
|
|
4
4
|
import { closeSync, existsSync, fstatSync, ftruncateSync, mkdirSync, openSync, readFileSync, readdirSync, writeSync } from "node:fs";
|
|
@@ -9828,6 +9828,53 @@ function commentOf(body) {
|
|
|
9828
9828
|
default: return null;
|
|
9829
9829
|
}
|
|
9830
9830
|
}
|
|
9831
|
+
/** The kinds whose rows wait in one of `spaces`. */
|
|
9832
|
+
function kindsIn(spaces) {
|
|
9833
|
+
return OUTBOX_KINDS.filter((kind) => {
|
|
9834
|
+
const space = KIND_SPACE[kind];
|
|
9835
|
+
return space !== null && spaces.includes(space);
|
|
9836
|
+
});
|
|
9837
|
+
}
|
|
9838
|
+
/**
|
|
9839
|
+
* Raises the gateway's one transport item while it has health rows and clears it once none are
|
|
9840
|
+
* left. Call it in the transaction that changed the rows, only when they changed.
|
|
9841
|
+
*/
|
|
9842
|
+
async function syncTransportItem(trx, gateway, at) {
|
|
9843
|
+
const itemKey = `transport:${gateway}`;
|
|
9844
|
+
const rows = await trx.selectFrom("transport_health").select(["reason", "since"]).where("gateway", "=", gateway).orderBy("since").orderBy("space").execute();
|
|
9845
|
+
const first = rows[0];
|
|
9846
|
+
if (first !== void 0) {
|
|
9847
|
+
const reason = rows.find((r) => r.reason !== "down")?.reason ?? "connection lost";
|
|
9848
|
+
const text = `${gateway} delivery down since ${first.since} (${reason})`;
|
|
9849
|
+
const item = await raiseItem(trx, {
|
|
9850
|
+
itemKey,
|
|
9851
|
+
kind: "transport_health",
|
|
9852
|
+
text,
|
|
9853
|
+
url: "",
|
|
9854
|
+
taskId: null,
|
|
9855
|
+
blockId: null,
|
|
9856
|
+
priority: 1,
|
|
9857
|
+
suppressFeed: true,
|
|
9858
|
+
at
|
|
9859
|
+
});
|
|
9860
|
+
if (item.text !== text) await trx.updateTable("operator_item").set({ text }).where("id", "=", item.id).execute();
|
|
9861
|
+
return;
|
|
9862
|
+
}
|
|
9863
|
+
const active = await trx.selectFrom("operator_item").select("id").where("item_key", "=", itemKey).where("cleared_at", "is", null).executeTakeFirst();
|
|
9864
|
+
if (active !== void 0) await clearItem(trx, active.id, at);
|
|
9865
|
+
}
|
|
9866
|
+
/**
|
|
9867
|
+
* Makes the next tick probe every space of `gateway` that has a health row: the probe clock is
|
|
9868
|
+
* set one interval back and waiting rows there become due at `at` (ISO-8601 UTC). Returns the
|
|
9869
|
+
* number of spaces armed; 0 when no outage is recorded.
|
|
9870
|
+
*/
|
|
9871
|
+
async function retryTransport(trx, gateway, at) {
|
|
9872
|
+
const rows = await trx.updateTable("transport_health").set({ probe_at: (/* @__PURE__ */ new Date(Date.parse(at) - HEALTH_PROBE_MS)).toISOString() }).where("gateway", "=", gateway).returning("space").execute();
|
|
9873
|
+
const kinds = kindsIn(rows.map((row) => row.space));
|
|
9874
|
+
if (kinds.length === 0) return rows.length;
|
|
9875
|
+
await trx.updateTable("outbox").set({ next_at: at }).where("state", "=", "pending").where("kind", "in", kinds).where("next_at", ">", at).execute();
|
|
9876
|
+
return rows.length;
|
|
9877
|
+
}
|
|
9831
9878
|
async function setComment(trx, body, patch) {
|
|
9832
9879
|
const commentId = commentOf(body);
|
|
9833
9880
|
if (commentId === null) return;
|
|
@@ -9912,6 +9959,8 @@ function createSender(options) {
|
|
|
9912
9959
|
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
9913
9960
|
const spaceIntervalMs = options.spaceIntervalMs ?? SPACE_INTERVAL_MS;
|
|
9914
9961
|
let downSince;
|
|
9962
|
+
/** Set once this outage's down rows were written; reset on `up`. */
|
|
9963
|
+
let downRecorded = false;
|
|
9915
9964
|
/** The last tick started; a new tick runs after it, so two ticks never interleave. */
|
|
9916
9965
|
let ticking = Promise.resolve();
|
|
9917
9966
|
/** Sets the comments of `rows` that carry one to `superseded`. */
|
|
@@ -9947,14 +9996,14 @@ function createSender(options) {
|
|
|
9947
9996
|
* there, so a row that retried before the outage gets a full window once the space recovers.
|
|
9948
9997
|
*/
|
|
9949
9998
|
async function suspendWindows(trx, spaces) {
|
|
9950
|
-
const kinds =
|
|
9951
|
-
const space = KIND_SPACE[kind];
|
|
9952
|
-
return space !== null && spaces.includes(space);
|
|
9953
|
-
});
|
|
9999
|
+
const kinds = kindsIn(spaces);
|
|
9954
10000
|
if (kinds.length === 0) return;
|
|
9955
10001
|
await trx.updateTable("outbox").set({ first_attempt_at: null }).where("state", "in", ["pending", "in_flight"]).where("kind", "in", kinds).execute();
|
|
9956
10002
|
}
|
|
9957
|
-
/**
|
|
10003
|
+
/**
|
|
10004
|
+
* tx 1: cancels stale rows, raises down-health and claims at most one due row per lane.
|
|
10005
|
+
* `ranThreshold` says the outage's down rows were written; the caller records that after commit.
|
|
10006
|
+
*/
|
|
9958
10007
|
async function claim(trx, at) {
|
|
9959
10008
|
const nowIso = new Date(at).toISOString();
|
|
9960
10009
|
await reconcileDoorbells(trx, new Date(at), options.config);
|
|
@@ -9978,16 +10027,19 @@ function createSender(options) {
|
|
|
9978
10027
|
}
|
|
9979
10028
|
await kill(trx, stale, "superseded");
|
|
9980
10029
|
await cascade(trx);
|
|
9981
|
-
|
|
10030
|
+
let ranThreshold = false;
|
|
10031
|
+
if (gateway !== void 0 && !downRecorded && downSince !== void 0 && at - downSince > DOWN_THRESHOLD_MS) {
|
|
9982
10032
|
const since = new Date(downSince).toISOString();
|
|
9983
|
-
await trx.insertInto("transport_health").values(SPACE_IDS.map((space) => ({
|
|
10033
|
+
const inserted = await trx.insertInto("transport_health").values(SPACE_IDS.map((space) => ({
|
|
9984
10034
|
gateway: gatewayKind,
|
|
9985
10035
|
space,
|
|
9986
10036
|
reason: "down",
|
|
9987
10037
|
since,
|
|
9988
10038
|
probe_at: since
|
|
9989
|
-
}))).onConflict((oc) => oc.columns(["gateway", "space"]).doNothing()).execute();
|
|
10039
|
+
}))).onConflict((oc) => oc.columns(["gateway", "space"]).doNothing()).returning("space").execute();
|
|
9990
10040
|
await suspendWindows(trx, SPACE_IDS);
|
|
10041
|
+
if (inserted.length > 0) await syncTransportItem(trx, gatewayKind, new Date(at));
|
|
10042
|
+
ranThreshold = true;
|
|
9991
10043
|
}
|
|
9992
10044
|
const live = await trx.selectFrom("outbox").leftJoin("outbox as parent", "parent.id", "outbox.parent_outbox_id").selectAll("outbox").select(["parent.state as parentState", "parent.external_ref as parentRef"]).where("outbox.state", "=", "pending").where("outbox.next_at", "<=", nowIso).execute();
|
|
9993
10045
|
const sentAt = new Map((gateway === void 0 ? [] : await trx.selectFrom("space_slot").select(["space", "sent_at"]).where("gateway", "=", gatewayKind).execute()).map((slot) => [slot.space, Date.parse(slot.sent_at)]));
|
|
@@ -10039,7 +10091,10 @@ function createSender(options) {
|
|
|
10039
10091
|
attempts: eb("attempts", "+", 1),
|
|
10040
10092
|
attempt_started: 0
|
|
10041
10093
|
})).where("id", "=", c.id).execute();
|
|
10042
|
-
return
|
|
10094
|
+
return {
|
|
10095
|
+
claims: claims.sort((a, b) => Number(a.space === null) - Number(b.space === null)),
|
|
10096
|
+
ranThreshold
|
|
10097
|
+
};
|
|
10043
10098
|
}
|
|
10044
10099
|
/**
|
|
10045
10100
|
* Stamps the start of a send, just before its IO: the row's retry window if none is open and,
|
|
@@ -10227,7 +10282,9 @@ function createSender(options) {
|
|
|
10227
10282
|
conversation_ref: conversation,
|
|
10228
10283
|
message_ref: outcome.message
|
|
10229
10284
|
});
|
|
10230
|
-
if (space !== null)
|
|
10285
|
+
if (space !== null) {
|
|
10286
|
+
if ((await trx.deleteFrom("transport_health").where("gateway", "=", gatewayKind).where("space", "=", space).returning("space").execute()).length > 0) await syncTransportItem(trx, gatewayKind, new Date(at));
|
|
10287
|
+
}
|
|
10231
10288
|
return;
|
|
10232
10289
|
}
|
|
10233
10290
|
case "retry": {
|
|
@@ -10252,13 +10309,13 @@ function createSender(options) {
|
|
|
10252
10309
|
}
|
|
10253
10310
|
case "failed":
|
|
10254
10311
|
if (outcome.scope === "message" || space === null) return postFailed({ last_error: outcome.reason });
|
|
10255
|
-
await trx.insertInto("transport_health").values({
|
|
10312
|
+
if ((await trx.insertInto("transport_health").values({
|
|
10256
10313
|
gateway: gatewayKind,
|
|
10257
10314
|
space,
|
|
10258
10315
|
reason: outcome.reason,
|
|
10259
10316
|
since: nowIso,
|
|
10260
10317
|
probe_at: new Date(startedAt).toISOString()
|
|
10261
|
-
}).onConflict((oc) => oc.columns(["gateway", "space"]).
|
|
10318
|
+
}).onConflict((oc) => oc.columns(["gateway", "space"]).doUpdateSet({ reason: outcome.reason }).where("transport_health.reason", "=", "down")).returning("space").execute()).length > 0) await syncTransportItem(trx, gatewayKind, new Date(at));
|
|
10262
10319
|
await suspendWindows(trx, [space]);
|
|
10263
10320
|
await row.set({
|
|
10264
10321
|
state: "pending",
|
|
@@ -10272,7 +10329,8 @@ function createSender(options) {
|
|
|
10272
10329
|
async function runTick(signal) {
|
|
10273
10330
|
if (signal?.aborted) return;
|
|
10274
10331
|
const claimedAt = now().getTime();
|
|
10275
|
-
const claims = await scanTransaction(store, (trx) => claim(trx, claimedAt));
|
|
10332
|
+
const { claims, ranThreshold } = await scanTransaction(store, (trx) => claim(trx, claimedAt));
|
|
10333
|
+
if (ranThreshold) downRecorded = true;
|
|
10276
10334
|
for (const [i, c] of claims.entries()) {
|
|
10277
10335
|
if (signal?.aborted) {
|
|
10278
10336
|
const ids = claims.slice(i).map((c) => c.id);
|
|
@@ -10325,8 +10383,20 @@ function createSender(options) {
|
|
|
10325
10383
|
return run;
|
|
10326
10384
|
},
|
|
10327
10385
|
recordConnection(state, at) {
|
|
10328
|
-
|
|
10329
|
-
|
|
10386
|
+
const run = ticking.then(async () => {
|
|
10387
|
+
if (state === "down") {
|
|
10388
|
+
downSince ??= at.getTime();
|
|
10389
|
+
return;
|
|
10390
|
+
}
|
|
10391
|
+
downSince = void 0;
|
|
10392
|
+
downRecorded = false;
|
|
10393
|
+
if (gateway === void 0) return;
|
|
10394
|
+
await scanTransaction(store, async (trx) => {
|
|
10395
|
+
if ((await trx.deleteFrom("transport_health").where("gateway", "=", gatewayKind).where("reason", "=", "down").returning("space").execute()).length > 0) await syncTransportItem(trx, gatewayKind, at);
|
|
10396
|
+
});
|
|
10397
|
+
});
|
|
10398
|
+
ticking = run.catch(() => void 0);
|
|
10399
|
+
return run;
|
|
10330
10400
|
}
|
|
10331
10401
|
};
|
|
10332
10402
|
}
|
|
@@ -10352,7 +10422,10 @@ function view(item, task) {
|
|
|
10352
10422
|
function notApplicable(hint) {
|
|
10353
10423
|
return new CommandError("retry_not_applicable", hint);
|
|
10354
10424
|
}
|
|
10355
|
-
/**
|
|
10425
|
+
/**
|
|
10426
|
+
* Item actions: retry runs the action behind the item's kind; close dismisses it. Both clear it,
|
|
10427
|
+
* except a retry on a transport item, which the sender clears when the transport is back.
|
|
10428
|
+
*/
|
|
10356
10429
|
function createItemMutations(deps) {
|
|
10357
10430
|
async function retryAction(trx, item, at) {
|
|
10358
10431
|
const closeHint = `close it with \`board item close I${String(item.id)}\`.`;
|
|
@@ -10382,6 +10455,10 @@ function createItemMutations(deps) {
|
|
|
10382
10455
|
const { item, task } = await loadItem(trx, input.item);
|
|
10383
10456
|
if (task?.membership === "archived") throw new CommandError("task_archived", "Add the task again before retrying its item.");
|
|
10384
10457
|
if (item.cleared_at !== null) throw notApplicable("The item is already cleared.");
|
|
10458
|
+
if (item.kind === "transport_health") {
|
|
10459
|
+
if (await retryTransport(trx, item.item_key.slice(10), ctx.at) === 0) throw notApplicable("No transport outage is recorded; the item clears on its own.");
|
|
10460
|
+
return view(item, task);
|
|
10461
|
+
}
|
|
10385
10462
|
await retryAction(trx, item, ctx.at);
|
|
10386
10463
|
await clearItem(trx, item.id, new Date(ctx.at), ctx.actor);
|
|
10387
10464
|
return view(item, task);
|
|
@@ -10617,7 +10694,7 @@ function interval(config, source) {
|
|
|
10617
10694
|
}
|
|
10618
10695
|
/** Reads the board's latest facts, items and source availability. */
|
|
10619
10696
|
async function getStatus(deps, projectId, visibleProjects, includeBoardItems = visibleProjects === void 0) {
|
|
10620
|
-
const { store, config, clock, completeSince } = deps;
|
|
10697
|
+
const { store, config, clock, completeSince, gateways = [] } = deps;
|
|
10621
10698
|
const projects = projectId === void 0 ? config.projects.filter((project) => visibleProjects === void 0 || visibleProjects.has(project.id)) : config.projects.filter((project) => project.id === projectId && (visibleProjects === void 0 || visibleProjects.has(project.id)));
|
|
10622
10699
|
if (projects.length === 0) throw new CommandError("not_found", "Check the project ID.");
|
|
10623
10700
|
const projectIds = new Set(projects.map((project) => project.id));
|
|
@@ -10690,6 +10767,23 @@ async function getStatus(deps, projectId, visibleProjects, includeBoardItems = v
|
|
|
10690
10767
|
});
|
|
10691
10768
|
const host = await store.db.selectFrom("meta").select("value").where("key", "=", "host_load_episode").executeTakeFirst();
|
|
10692
10769
|
const spur = sourceById.get("spur");
|
|
10770
|
+
const healthRows = await store.db.selectFrom("transport_health").select([
|
|
10771
|
+
"gateway",
|
|
10772
|
+
"space",
|
|
10773
|
+
"reason",
|
|
10774
|
+
"since"
|
|
10775
|
+
]).orderBy("since").orderBy("space").execute();
|
|
10776
|
+
const transport = [.../* @__PURE__ */ new Set([...gateways, ...healthRows.map((row) => row.gateway)])].sort().map((gateway) => {
|
|
10777
|
+
const down = healthRows.filter((row) => row.gateway === gateway);
|
|
10778
|
+
const first = down[0];
|
|
10779
|
+
return {
|
|
10780
|
+
gateway,
|
|
10781
|
+
state: first === void 0 ? "up" : "down",
|
|
10782
|
+
since: first?.since ?? null,
|
|
10783
|
+
reason: first?.reason ?? null,
|
|
10784
|
+
spaces: down.map((row) => row.space).sort()
|
|
10785
|
+
};
|
|
10786
|
+
});
|
|
10693
10787
|
return {
|
|
10694
10788
|
tasks,
|
|
10695
10789
|
items,
|
|
@@ -10698,7 +10792,8 @@ async function getStatus(deps, projectId, visibleProjects, includeBoardItems = v
|
|
|
10698
10792
|
sources,
|
|
10699
10793
|
warmingUp: sources.some((source) => source.warmingUp),
|
|
10700
10794
|
spurUnavailable: spur?.blind_reason === null || spur === void 0 ? null : `Spur unavailable: ${spur.blind_message ?? spur.blind_reason}`,
|
|
10701
|
-
hostLoad: host?.value !== void 0 && host.value !== "null" ? "Host under load" : null
|
|
10795
|
+
hostLoad: host?.value !== void 0 && host.value !== "null" ? "Host under load" : null,
|
|
10796
|
+
transport
|
|
10702
10797
|
};
|
|
10703
10798
|
}
|
|
10704
10799
|
//#endregion
|
|
@@ -10892,7 +10987,7 @@ function createTaskQueries(config) {
|
|
|
10892
10987
|
const catalog = await buildCatalogSnapshot(OPERATIONS);
|
|
10893
10988
|
/** Concrete handlers shared by the daemon's HTTP and inbound transports. */
|
|
10894
10989
|
function createOperationRegistry(deps) {
|
|
10895
|
-
const { config, clock, completeSince } = deps;
|
|
10990
|
+
const { config, clock, completeSince, gateways } = deps;
|
|
10896
10991
|
const items = createItemMutations(deps);
|
|
10897
10992
|
const membership = createMembershipMutations(config);
|
|
10898
10993
|
const links = createLinkMutations(config);
|
|
@@ -10940,7 +11035,8 @@ function createOperationRegistry(deps) {
|
|
|
10940
11035
|
store: { db: trx },
|
|
10941
11036
|
config,
|
|
10942
11037
|
clock,
|
|
10943
|
-
completeSince
|
|
11038
|
+
completeSince,
|
|
11039
|
+
gateways
|
|
10944
11040
|
}, input.project, visibleProjects(config, actor), authorize([{ grant: "operator" }], actor, {
|
|
10945
11041
|
config,
|
|
10946
11042
|
target: { kind: "missing" }
|
|
@@ -16242,7 +16338,10 @@ function slackSettings(config, secrets) {
|
|
|
16242
16338
|
requestTimeoutMs: DELIVERY_REQUEST_TIMEOUT_MS
|
|
16243
16339
|
};
|
|
16244
16340
|
}
|
|
16245
|
-
/**
|
|
16341
|
+
/**
|
|
16342
|
+
* Own the timer and wait for the last queued sender tick and connection update before the store
|
|
16343
|
+
* closes.
|
|
16344
|
+
*/
|
|
16246
16345
|
function createOutboxModule(ctx, gateway, options = {}) {
|
|
16247
16346
|
const spur = options.spur ?? createSpurPort();
|
|
16248
16347
|
const sender = createSender({
|
|
@@ -16254,6 +16353,7 @@ function createOutboxModule(ctx, gateway, options = {}) {
|
|
|
16254
16353
|
});
|
|
16255
16354
|
let timer;
|
|
16256
16355
|
let active = Promise.resolve();
|
|
16356
|
+
let updating = Promise.resolve();
|
|
16257
16357
|
let stopped = false;
|
|
16258
16358
|
let signal;
|
|
16259
16359
|
const schedule = () => {
|
|
@@ -16282,8 +16382,17 @@ function createOutboxModule(ctx, gateway, options = {}) {
|
|
|
16282
16382
|
stopped = true;
|
|
16283
16383
|
if (timer !== void 0) clearTimeout(timer);
|
|
16284
16384
|
await active;
|
|
16385
|
+
await updating;
|
|
16285
16386
|
},
|
|
16286
|
-
pulse: tick
|
|
16387
|
+
pulse: tick,
|
|
16388
|
+
connection(state) {
|
|
16389
|
+
if (stopped) return Promise.resolve();
|
|
16390
|
+
const update = sender.recordConnection(state, new Date(ctx.clock.now())).catch((err) => {
|
|
16391
|
+
ctx.log.error({ err }, "transport state update failed");
|
|
16392
|
+
});
|
|
16393
|
+
updating = Promise.all([updating, update]).then(() => void 0);
|
|
16394
|
+
return update;
|
|
16395
|
+
}
|
|
16287
16396
|
};
|
|
16288
16397
|
}
|
|
16289
16398
|
//#endregion
|
|
@@ -16401,10 +16510,14 @@ function createDaemonModules(ctxFor, ports = {}) {
|
|
|
16401
16510
|
clock: board.clock,
|
|
16402
16511
|
...ports.productVersion === void 0 ? {} : { productVersion: ports.productVersion }
|
|
16403
16512
|
});
|
|
16513
|
+
const ctx = ctxFor("gateway-slack");
|
|
16514
|
+
const settings = slackSettings(ctx.config, ctx.secrets);
|
|
16515
|
+
const gateway = ports.gateway ?? (settings === null ? void 0 : createSlackGateway(settings));
|
|
16404
16516
|
const registry = createOperationRegistry({
|
|
16405
16517
|
config: board.config,
|
|
16406
16518
|
clock: board.clock,
|
|
16407
16519
|
completeSince: core.completeSince,
|
|
16520
|
+
gateways: gateway === void 0 ? [] : [gateway.kind],
|
|
16408
16521
|
reapplyInbound: () => inbound?.reapply
|
|
16409
16522
|
});
|
|
16410
16523
|
const notifier = createSystemdNotifier({
|
|
@@ -16435,16 +16548,16 @@ function createDaemonModules(ctxFor, ports = {}) {
|
|
|
16435
16548
|
notifier.watchdog();
|
|
16436
16549
|
}
|
|
16437
16550
|
}));
|
|
16438
|
-
const ctx = ctxFor("gateway-slack");
|
|
16439
|
-
const settings = slackSettings(ctx.config, ctx.secrets);
|
|
16440
|
-
const gateway = ports.gateway ?? (settings === null ? void 0 : createSlackGateway(settings));
|
|
16441
16551
|
const inbound = gateway === void 0 || ports.gateway === void 0 && settings?.appToken === void 0 ? void 0 : createInboundModule(ctxFor("inbound"), gateway, registry);
|
|
16442
16552
|
if (settings !== null && inbound === void 0) ctx.log.info("Slack inbound disabled: SLACK_APP_TOKEN is absent");
|
|
16553
|
+
const outbox = createOutboxModule(ctxFor("outbox"), gateway, ports.spur === void 0 ? {} : { spur: ports.spur });
|
|
16443
16554
|
if (gateway !== void 0) entries.push({
|
|
16444
16555
|
name: "gateway-slack",
|
|
16445
16556
|
stopStage: "final",
|
|
16446
16557
|
module: {
|
|
16447
|
-
start: () => gateway.start(
|
|
16558
|
+
start: () => gateway.start(async (event) => {
|
|
16559
|
+
await Promise.all([inbound?.sink(event), event.kind === "connection" ? outbox.connection(event.state) : void 0]);
|
|
16560
|
+
}),
|
|
16448
16561
|
async stop() {
|
|
16449
16562
|
try {
|
|
16450
16563
|
await gateway.stop();
|
|
@@ -16459,7 +16572,6 @@ function createDaemonModules(ctxFor, ports = {}) {
|
|
|
16459
16572
|
stopStage: "pollers",
|
|
16460
16573
|
module: inbound
|
|
16461
16574
|
});
|
|
16462
|
-
const outbox = createOutboxModule(ctxFor("outbox"), gateway, ports.spur === void 0 ? {} : { spur: ports.spur });
|
|
16463
16575
|
entries.push({
|
|
16464
16576
|
name: "outbox",
|
|
16465
16577
|
stopStage: "outbox",
|
package/dist/mcp.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { N as ERRORS, f as renderMcpError, l as OPERATIONS } from "./src-
|
|
2
|
-
import { i as CliError, n as requestOperation } from "./client-
|
|
3
|
-
import { a as memoryVerdictCache, r as compareCatalogs, s as loadClientConfig, t as checkCatalog } from "./config-
|
|
1
|
+
import { N as ERRORS, f as renderMcpError, l as OPERATIONS } from "./src-DyDmiehC.mjs";
|
|
2
|
+
import { i as CliError, n as requestOperation } from "./client-C4Ee3Gfs.mjs";
|
|
3
|
+
import { a as memoryVerdictCache, r as compareCatalogs, s as loadClientConfig, t as checkCatalog } from "./config-BmT8hPi8.mjs";
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
|
5
5
|
import { readFileSync } from "node:fs";
|
|
6
6
|
import { z } from "zod";
|
|
@@ -1638,7 +1638,14 @@ const OPERATIONS = [
|
|
|
1638
1638
|
sources: z.array(sourceViewSchema).describe("Freshness and availability of each source."),
|
|
1639
1639
|
warmingUp: z.boolean().describe("Whether required sources still need a complete poll."),
|
|
1640
1640
|
spurUnavailable: z.string().nullable().describe("One combined Spur availability line, or null when available."),
|
|
1641
|
-
hostLoad: z.string().nullable().describe("Host-load episode line, or null when absent.")
|
|
1641
|
+
hostLoad: z.string().nullable().describe("Host-load episode line, or null when absent."),
|
|
1642
|
+
transport: z.array(z.strictObject({
|
|
1643
|
+
gateway: z.string().min(1).describe("Chat gateway kind."),
|
|
1644
|
+
state: z.enum(["up", "down"]).describe("down while delivery to any space of the gateway is suspended."),
|
|
1645
|
+
since: z.iso.datetime().nullable().describe("When the earliest outage started, ISO 8601 UTC, or null when up."),
|
|
1646
|
+
reason: z.string().nullable().describe("Reason of the earliest outage, or null when up."),
|
|
1647
|
+
spaces: z.array(z.enum(SPACE_IDS)).describe("Spaces whose delivery is suspended.")
|
|
1648
|
+
}).describe("Transport health of one gateway.")).describe("Transport health of each chat gateway.")
|
|
1642
1649
|
}),
|
|
1643
1650
|
grants: operatorOrProjectAgent,
|
|
1644
1651
|
errors: ["not_found"],
|
|
@@ -1667,7 +1674,8 @@ const OPERATIONS = [
|
|
|
1667
1674
|
sources: [],
|
|
1668
1675
|
warmingUp: true,
|
|
1669
1676
|
spurUnavailable: null,
|
|
1670
|
-
hostLoad: null
|
|
1677
|
+
hostLoad: null,
|
|
1678
|
+
transport: []
|
|
1671
1679
|
}
|
|
1672
1680
|
}]
|
|
1673
1681
|
}),
|