@gugu910/pi-slack-bridge 0.1.3

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 (42) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +299 -0
  3. package/dist/activity-log.js +304 -0
  4. package/dist/broker/adapters/slack.js +645 -0
  5. package/dist/broker/adapters/types.js +3 -0
  6. package/dist/broker/agent-messaging.js +154 -0
  7. package/dist/broker/auth.js +97 -0
  8. package/dist/broker/client.js +495 -0
  9. package/dist/broker/control-plane-canvas.js +357 -0
  10. package/dist/broker/index.js +125 -0
  11. package/dist/broker/leader.js +133 -0
  12. package/dist/broker/maintenance.js +135 -0
  13. package/dist/broker/paths.js +69 -0
  14. package/dist/broker/router.js +287 -0
  15. package/dist/broker/schema.js +1492 -0
  16. package/dist/broker/socket-server.js +665 -0
  17. package/dist/broker/types.js +12 -0
  18. package/dist/broker-delivery.js +34 -0
  19. package/dist/canvases.js +175 -0
  20. package/dist/deploy-manifest.js +238 -0
  21. package/dist/follower-delivery.js +83 -0
  22. package/dist/git-metadata.js +95 -0
  23. package/dist/guardrails.js +197 -0
  24. package/dist/helpers.js +2128 -0
  25. package/dist/home-tab.js +240 -0
  26. package/dist/index.js +3086 -0
  27. package/dist/pinet-commands.js +244 -0
  28. package/dist/ralph-loop.js +385 -0
  29. package/dist/reaction-triggers.js +160 -0
  30. package/dist/scheduled-wakeups.js +71 -0
  31. package/dist/slack-api.js +5 -0
  32. package/dist/slack-block-kit.js +425 -0
  33. package/dist/slack-export.js +214 -0
  34. package/dist/slack-modals.js +269 -0
  35. package/dist/slack-presence.js +98 -0
  36. package/dist/slack-socket-dedup.js +143 -0
  37. package/dist/slack-tools.js +1715 -0
  38. package/dist/slack-upload.js +147 -0
  39. package/dist/task-assignments.js +403 -0
  40. package/dist/ttl-cache.js +110 -0
  41. package/manifest.yaml +57 -0
  42. package/package.json +45 -0
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createBrokerDeliveryState = createBrokerDeliveryState;
4
+ exports.resetBrokerDeliveryState = resetBrokerDeliveryState;
5
+ exports.isBrokerInboxIdTracked = isBrokerInboxIdTracked;
6
+ exports.queueBrokerInboxIds = queueBrokerInboxIds;
7
+ exports.markBrokerInboxIdsHandled = markBrokerInboxIdsHandled;
8
+ exports.getBrokerInboxIds = getBrokerInboxIds;
9
+ function createBrokerDeliveryState() {
10
+ return {
11
+ pendingInboxIds: new Set(),
12
+ };
13
+ }
14
+ function resetBrokerDeliveryState(state) {
15
+ state.pendingInboxIds.clear();
16
+ }
17
+ function isBrokerInboxIdTracked(state, inboxId) {
18
+ return state.pendingInboxIds.has(inboxId);
19
+ }
20
+ function queueBrokerInboxIds(state, inboxIds) {
21
+ for (const inboxId of inboxIds) {
22
+ state.pendingInboxIds.add(inboxId);
23
+ }
24
+ }
25
+ function markBrokerInboxIdsHandled(state, inboxIds) {
26
+ for (const inboxId of inboxIds) {
27
+ state.pendingInboxIds.delete(inboxId);
28
+ }
29
+ }
30
+ function getBrokerInboxIds(messages) {
31
+ return [
32
+ ...new Set(messages.flatMap((message) => (message.brokerInboxId ? [message.brokerInboxId] : []))),
33
+ ];
34
+ }
@@ -0,0 +1,175 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeSlackCanvasCreateKind = normalizeSlackCanvasCreateKind;
4
+ exports.normalizeSlackCanvasUpdateMode = normalizeSlackCanvasUpdateMode;
5
+ exports.normalizeSlackCanvasSectionType = normalizeSlackCanvasSectionType;
6
+ exports.buildSlackCanvasCreateRequest = buildSlackCanvasCreateRequest;
7
+ exports.buildSlackCanvasEditRequest = buildSlackCanvasEditRequest;
8
+ exports.buildSlackCanvasSectionsLookupRequest = buildSlackCanvasSectionsLookupRequest;
9
+ exports.pickSlackCanvasSectionId = pickSlackCanvasSectionId;
10
+ exports.extractSlackChannelCanvasId = extractSlackChannelCanvasId;
11
+ exports.extractExistingChannelCanvasId = extractExistingChannelCanvasId;
12
+ function normalizeOptionalString(value) {
13
+ const trimmed = value?.trim();
14
+ return trimmed && trimmed.length > 0 ? trimmed : undefined;
15
+ }
16
+ function buildDocumentContent(markdown) {
17
+ if (markdown == null || markdown.length === 0)
18
+ return undefined;
19
+ return { type: "markdown", markdown };
20
+ }
21
+ function asRecord(value) {
22
+ return typeof value === "object" && value !== null ? value : null;
23
+ }
24
+ function asString(value) {
25
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
26
+ }
27
+ function asStringArray(value) {
28
+ if (!Array.isArray(value))
29
+ return undefined;
30
+ const strings = value.map(asString).filter((item) => Boolean(item));
31
+ return strings.length > 0 ? strings : undefined;
32
+ }
33
+ function normalizeSlackCanvasCreateKind(kind) {
34
+ const normalized = kind?.trim().toLowerCase();
35
+ if (!normalized)
36
+ return "standalone";
37
+ if (normalized === "standalone" || normalized === "channel") {
38
+ return normalized;
39
+ }
40
+ throw new Error("Unsupported canvas kind. Use 'standalone' or 'channel'.");
41
+ }
42
+ function normalizeSlackCanvasUpdateMode(mode) {
43
+ const normalized = mode?.trim().toLowerCase();
44
+ if (!normalized)
45
+ return "append";
46
+ if (normalized === "append" || normalized === "prepend" || normalized === "replace") {
47
+ return normalized;
48
+ }
49
+ throw new Error("Unsupported canvas update mode. Use 'append', 'prepend', or 'replace'.");
50
+ }
51
+ function normalizeSlackCanvasSectionType(type) {
52
+ const normalized = type?.trim().toLowerCase();
53
+ if (!normalized)
54
+ return undefined;
55
+ if (normalized === "h1" ||
56
+ normalized === "h2" ||
57
+ normalized === "h3" ||
58
+ normalized === "any_header") {
59
+ return normalized;
60
+ }
61
+ throw new Error("Unsupported canvas section type. Use 'h1', 'h2', 'h3', or 'any_header'.");
62
+ }
63
+ function buildSlackCanvasCreateRequest(input) {
64
+ const kind = normalizeSlackCanvasCreateKind(input.kind);
65
+ const channelId = normalizeOptionalString(input.channelId);
66
+ const title = normalizeOptionalString(input.title);
67
+ const documentContent = buildDocumentContent(input.markdown);
68
+ if (kind === "channel") {
69
+ if (!channelId) {
70
+ throw new Error("Channel canvases require a channel.");
71
+ }
72
+ return {
73
+ kind,
74
+ method: "conversations.canvases.create",
75
+ body: {
76
+ ...(title ? { title } : {}),
77
+ ...(documentContent ? { document_content: documentContent } : {}),
78
+ channel_id: channelId,
79
+ },
80
+ };
81
+ }
82
+ return {
83
+ kind,
84
+ method: "canvases.create",
85
+ body: {
86
+ ...(title ? { title } : {}),
87
+ ...(documentContent ? { document_content: documentContent } : {}),
88
+ ...(channelId ? { channel_id: channelId } : {}),
89
+ },
90
+ };
91
+ }
92
+ function buildSlackCanvasEditRequest(input) {
93
+ const canvasId = normalizeOptionalString(input.canvasId);
94
+ if (!canvasId) {
95
+ throw new Error("Canvas updates require a canvas ID.");
96
+ }
97
+ if (input.markdown.length === 0) {
98
+ throw new Error("Canvas updates require markdown content.");
99
+ }
100
+ const sectionId = normalizeOptionalString(input.sectionId);
101
+ const mode = normalizeSlackCanvasUpdateMode(input.mode);
102
+ const operation = mode === "append" ? "insert_at_end" : mode === "prepend" ? "insert_at_start" : "replace";
103
+ return {
104
+ canvas_id: canvasId,
105
+ changes: [
106
+ {
107
+ operation,
108
+ document_content: { type: "markdown", markdown: input.markdown },
109
+ ...(sectionId ? { section_id: sectionId } : {}),
110
+ },
111
+ ],
112
+ };
113
+ }
114
+ function buildSlackCanvasSectionsLookupRequest(input) {
115
+ const canvasId = normalizeOptionalString(input.canvasId);
116
+ if (!canvasId) {
117
+ throw new Error("Canvas section lookups require a canvas ID.");
118
+ }
119
+ const containsText = normalizeOptionalString(input.containsText);
120
+ if (!containsText) {
121
+ throw new Error("Canvas section lookups require text to match.");
122
+ }
123
+ const sectionType = normalizeSlackCanvasSectionType(input.sectionType);
124
+ return {
125
+ canvas_id: canvasId,
126
+ criteria: {
127
+ contains_text: containsText,
128
+ ...(sectionType ? { section_types: [sectionType] } : {}),
129
+ },
130
+ };
131
+ }
132
+ function pickSlackCanvasSectionId(sections, sectionIndex) {
133
+ const matches = (sections ?? [])
134
+ .map((section) => normalizeOptionalString(section.id))
135
+ .filter((id) => Boolean(id));
136
+ if (matches.length === 0) {
137
+ throw new Error("No canvas sections matched the lookup.");
138
+ }
139
+ if (sectionIndex != null) {
140
+ if (!Number.isInteger(sectionIndex) || sectionIndex < 1) {
141
+ throw new Error("section_index must be a positive integer.");
142
+ }
143
+ const selected = matches[sectionIndex - 1];
144
+ if (!selected) {
145
+ throw new Error(`Canvas section lookup matched ${matches.length} sections; section_index ${sectionIndex} is out of range.`);
146
+ }
147
+ return selected;
148
+ }
149
+ if (matches.length > 1) {
150
+ throw new Error(`Canvas section lookup matched ${matches.length} sections. Provide section_index to choose one result or narrow the lookup.`);
151
+ }
152
+ return matches[0];
153
+ }
154
+ function extractSlackChannelCanvasId(response) {
155
+ const channel = asRecord(response.channel);
156
+ const properties = asRecord(channel?.properties);
157
+ if (!properties)
158
+ return null;
159
+ const directCanvas = asString(properties.canvas);
160
+ if (directCanvas)
161
+ return directCanvas;
162
+ const canvasRecord = asRecord(properties.canvas);
163
+ const canvasId = asString(canvasRecord?.id) ?? asString(canvasRecord?.canvas_id);
164
+ if (canvasId)
165
+ return canvasId;
166
+ const channelSolutions = asRecord(properties.channel_solutions);
167
+ const canvasIds = asStringArray(channelSolutions?.canvas_ids);
168
+ if (canvasIds?.[0])
169
+ return canvasIds[0];
170
+ return null;
171
+ }
172
+ function extractExistingChannelCanvasId(response) {
173
+ const canvasId = extractSlackChannelCanvasId(response);
174
+ return canvasId;
175
+ }
@@ -0,0 +1,238 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.diffManifestScopes = diffManifestScopes;
37
+ exports.formatScopeChangeSummary = formatScopeChangeSummary;
38
+ exports.resolveDeployConfig = resolveDeployConfig;
39
+ exports.getDeployConfigError = getDeployConfigError;
40
+ exports.deploySlackManifest = deploySlackManifest;
41
+ exports.run = run;
42
+ const fs = __importStar(require("node:fs"));
43
+ const path = __importStar(require("node:path"));
44
+ const node_child_process_1 = require("node:child_process");
45
+ const node_util_1 = require("node:util");
46
+ const node_url_1 = require("node:url");
47
+ const execFile = (0, node_util_1.promisify)(node_child_process_1.execFile);
48
+ class SlackMethodError extends Error {
49
+ method;
50
+ slackError;
51
+ details;
52
+ constructor(method, slackError, details = []) {
53
+ const detailMessage = details
54
+ .map((detail) => {
55
+ const pointer = detail.pointer ? `${detail.pointer}: ` : "";
56
+ return `- ${pointer}${detail.message ?? "unknown error"}`;
57
+ })
58
+ .join("\n");
59
+ super(detailMessage
60
+ ? `Slack ${method}: ${slackError}\n${detailMessage}`
61
+ : `Slack ${method}: ${slackError}`);
62
+ this.name = "SlackMethodError";
63
+ this.method = method;
64
+ this.slackError = slackError;
65
+ this.details = details;
66
+ }
67
+ }
68
+ function loadSettings(settingsPath) {
69
+ const resolvedPath = settingsPath ??
70
+ path.join(process.env.HOME ?? process.env.USERPROFILE ?? "", ".pi", "agent", "settings.json");
71
+ try {
72
+ const content = fs.readFileSync(resolvedPath, "utf-8");
73
+ const parsed = JSON.parse(content);
74
+ return (parsed["slack-bridge"] ??
75
+ {});
76
+ }
77
+ catch {
78
+ return {};
79
+ }
80
+ }
81
+ function buildSlackRequest(method, token, body) {
82
+ const headers = {
83
+ Authorization: `Bearer ${token}`,
84
+ };
85
+ let serialized;
86
+ if (body) {
87
+ headers["Content-Type"] = "application/json; charset=utf-8";
88
+ serialized = JSON.stringify(body);
89
+ }
90
+ return {
91
+ url: `https://slack.com/api/${method}`,
92
+ init: {
93
+ method: "POST",
94
+ headers,
95
+ ...(serialized ? { body: serialized } : {}),
96
+ },
97
+ };
98
+ }
99
+ function asRecord(value) {
100
+ return typeof value === "object" && value !== null ? value : null;
101
+ }
102
+ function readScopeList(manifest, scopeType) {
103
+ const oauthConfig = asRecord(manifest?.oauth_config);
104
+ const scopes = asRecord(oauthConfig?.scopes);
105
+ const raw = scopes?.[scopeType];
106
+ if (!Array.isArray(raw)) {
107
+ return [];
108
+ }
109
+ return [
110
+ ...new Set(raw.filter((item) => typeof item === "string" && item.length > 0)),
111
+ ].sort();
112
+ }
113
+ function diffManifestScopes(beforeManifest, afterManifest) {
114
+ const beforeBot = new Set(readScopeList(beforeManifest, "bot"));
115
+ const afterBot = new Set(readScopeList(afterManifest, "bot"));
116
+ const beforeUser = new Set(readScopeList(beforeManifest, "user"));
117
+ const afterUser = new Set(readScopeList(afterManifest, "user"));
118
+ const diff = (next, prev) => [...next].filter((scope) => !prev.has(scope)).sort();
119
+ return {
120
+ addedBotScopes: diff(afterBot, beforeBot),
121
+ removedBotScopes: diff(beforeBot, afterBot),
122
+ addedUserScopes: diff(afterUser, beforeUser),
123
+ removedUserScopes: diff(beforeUser, afterUser),
124
+ };
125
+ }
126
+ function formatScopeChangeSummary(changes) {
127
+ const lines = [];
128
+ if (changes.addedBotScopes.length > 0) {
129
+ lines.push(`Bot scopes added: ${changes.addedBotScopes.join(", ")}`);
130
+ }
131
+ if (changes.removedBotScopes.length > 0) {
132
+ lines.push(`Bot scopes removed: ${changes.removedBotScopes.join(", ")}`);
133
+ }
134
+ if (changes.addedUserScopes.length > 0) {
135
+ lines.push(`User scopes added: ${changes.addedUserScopes.join(", ")}`);
136
+ }
137
+ if (changes.removedUserScopes.length > 0) {
138
+ lines.push(`User scopes removed: ${changes.removedUserScopes.join(", ")}`);
139
+ }
140
+ return lines.length > 0 ? lines : ["No scope changes."];
141
+ }
142
+ function resolveDeployConfig(settings, env, cwd = process.cwd()) {
143
+ return {
144
+ manifestPath: path.join(cwd, "slack-bridge", "manifest.yaml"),
145
+ appId: settings.appId ?? env.SLACK_APP_ID,
146
+ appConfigToken: settings.appConfigToken ?? env.SLACK_APP_CONFIG_TOKEN ?? env.SLACK_CONFIG_TOKEN,
147
+ appToken: settings.appToken ?? env.SLACK_APP_TOKEN,
148
+ };
149
+ }
150
+ function getDeployConfigError(config) {
151
+ if (!fs.existsSync(config.manifestPath)) {
152
+ return `Slack manifest not found at ${config.manifestPath}`;
153
+ }
154
+ const messages = [];
155
+ if (!config.appId) {
156
+ messages.push("Missing Slack app ID. Set slack-bridge.appId in ~/.pi/agent/settings.json or SLACK_APP_ID.");
157
+ }
158
+ if (!config.appConfigToken) {
159
+ const appTokenNote = config.appToken
160
+ ? " Note: slack-bridge.appToken / SLACK_APP_TOKEN is a Socket Mode xapp token and cannot be used with apps.manifest.update."
161
+ : "";
162
+ messages.push("Missing Slack app configuration token. Set slack-bridge.appConfigToken in ~/.pi/agent/settings.json or SLACK_APP_CONFIG_TOKEN (or SLACK_CONFIG_TOKEN)." +
163
+ appTokenNote);
164
+ }
165
+ return messages.length > 0 ? messages.join(" ") : null;
166
+ }
167
+ async function parseManifestYaml(manifestPath) {
168
+ try {
169
+ const program = [
170
+ "require 'yaml'",
171
+ "require 'json'",
172
+ "data = YAML.load_file(ARGV[0])",
173
+ "puts JSON.generate(data)",
174
+ ].join("; ");
175
+ const { stdout } = await execFile("ruby", ["-e", program, manifestPath]);
176
+ return JSON.parse(stdout);
177
+ }
178
+ catch (error) {
179
+ throw new Error(`Failed to parse ${manifestPath} via Ruby YAML parser. Ensure Ruby is installed and the manifest is valid YAML. ${String(error)}`);
180
+ }
181
+ }
182
+ async function callSlackMethod(method, token, body) {
183
+ const { url, init } = buildSlackRequest(method, token, body);
184
+ const response = await fetch(url, init);
185
+ const payload = (await response.json());
186
+ if (!response.ok || payload.ok !== true) {
187
+ throw new SlackMethodError(method, payload.error ?? `http_${response.status}`, payload.errors ?? []);
188
+ }
189
+ return payload;
190
+ }
191
+ async function exportRemoteManifest(appId, token) {
192
+ const payload = await callSlackMethod("apps.manifest.export", token, { app_id: appId });
193
+ return payload.manifest;
194
+ }
195
+ async function validateManifest(manifest, token) {
196
+ await callSlackMethod("apps.manifest.validate", token, {
197
+ manifest: JSON.stringify(manifest),
198
+ });
199
+ }
200
+ async function updateManifest(appId, manifest, token) {
201
+ await callSlackMethod("apps.manifest.update", token, {
202
+ app_id: appId,
203
+ manifest: JSON.stringify(manifest),
204
+ });
205
+ }
206
+ async function deploySlackManifest(config) {
207
+ const configError = getDeployConfigError(config);
208
+ if (configError) {
209
+ throw new Error(configError);
210
+ }
211
+ const appId = config.appId;
212
+ const appConfigToken = config.appConfigToken;
213
+ const manifest = await parseManifestYaml(config.manifestPath);
214
+ const previousManifest = await exportRemoteManifest(appId, appConfigToken);
215
+ await validateManifest(manifest, appConfigToken);
216
+ await updateManifest(appId, manifest, appConfigToken);
217
+ const updatedManifest = await exportRemoteManifest(appId, appConfigToken);
218
+ return {
219
+ appId,
220
+ scopeChanges: diffManifestScopes(previousManifest, updatedManifest),
221
+ };
222
+ }
223
+ async function run() {
224
+ const settingsPath = process.env.PI_SETTINGS_PATH;
225
+ const settings = loadSettings(settingsPath);
226
+ const config = resolveDeployConfig(settings, process.env);
227
+ const result = await deploySlackManifest(config);
228
+ console.log(`Updated Slack app ${result.appId} from ${config.manifestPath}`);
229
+ for (const line of formatScopeChangeSummary(result.scopeChanges)) {
230
+ console.log(line);
231
+ }
232
+ }
233
+ if (process.argv[1] && import.meta.url === (0, node_url_1.pathToFileURL)(process.argv[1]).href) {
234
+ run().catch((error) => {
235
+ console.error(error instanceof Error ? error.message : String(error));
236
+ process.exitCode = 1;
237
+ });
238
+ }
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createFollowerDeliveryState = createFollowerDeliveryState;
4
+ exports.resetFollowerDeliveryState = resetFollowerDeliveryState;
5
+ exports.isFollowerInboxIdTracked = isFollowerInboxIdTracked;
6
+ exports.queueFollowerInboxIds = queueFollowerInboxIds;
7
+ exports.markFollowerInboxIdsDelivered = markFollowerInboxIdsDelivered;
8
+ exports.hasDeliveredFollowerInboxIds = hasDeliveredFollowerInboxIds;
9
+ exports.takeFollowerAckBatch = takeFollowerAckBatch;
10
+ exports.markFollowerAckBatchSucceeded = markFollowerAckBatchSucceeded;
11
+ exports.markFollowerAckBatchFailed = markFollowerAckBatchFailed;
12
+ exports.drainFollowerAckBatches = drainFollowerAckBatches;
13
+ exports.getFollowerShutdownAckIds = getFollowerShutdownAckIds;
14
+ function createFollowerDeliveryState() {
15
+ return {
16
+ queuedButUndeliveredIds: new Set(),
17
+ deliveredAwaitingAckIds: new Set(),
18
+ ackInFlightIds: new Set(),
19
+ };
20
+ }
21
+ function resetFollowerDeliveryState(state) {
22
+ state.queuedButUndeliveredIds.clear();
23
+ state.deliveredAwaitingAckIds.clear();
24
+ state.ackInFlightIds.clear();
25
+ }
26
+ function isFollowerInboxIdTracked(state, inboxId) {
27
+ return (state.queuedButUndeliveredIds.has(inboxId) ||
28
+ state.deliveredAwaitingAckIds.has(inboxId) ||
29
+ state.ackInFlightIds.has(inboxId));
30
+ }
31
+ function queueFollowerInboxIds(state, inboxIds) {
32
+ for (const inboxId of inboxIds) {
33
+ state.queuedButUndeliveredIds.add(inboxId);
34
+ }
35
+ }
36
+ function markFollowerInboxIdsDelivered(state, inboxIds) {
37
+ for (const inboxId of inboxIds) {
38
+ state.queuedButUndeliveredIds.delete(inboxId);
39
+ state.deliveredAwaitingAckIds.add(inboxId);
40
+ }
41
+ }
42
+ function hasDeliveredFollowerInboxIds(state) {
43
+ return state.deliveredAwaitingAckIds.size > 0;
44
+ }
45
+ function takeFollowerAckBatch(state) {
46
+ const batch = [...state.deliveredAwaitingAckIds];
47
+ for (const inboxId of batch) {
48
+ state.deliveredAwaitingAckIds.delete(inboxId);
49
+ state.ackInFlightIds.add(inboxId);
50
+ }
51
+ return batch;
52
+ }
53
+ function markFollowerAckBatchSucceeded(state, inboxIds) {
54
+ for (const inboxId of inboxIds) {
55
+ state.ackInFlightIds.delete(inboxId);
56
+ }
57
+ }
58
+ function markFollowerAckBatchFailed(state, inboxIds) {
59
+ for (const inboxId of inboxIds) {
60
+ if (state.ackInFlightIds.delete(inboxId)) {
61
+ state.deliveredAwaitingAckIds.add(inboxId);
62
+ }
63
+ }
64
+ }
65
+ async function drainFollowerAckBatches(state, ackBatch) {
66
+ while (true) {
67
+ const batch = takeFollowerAckBatch(state);
68
+ if (batch.length === 0) {
69
+ return;
70
+ }
71
+ try {
72
+ await ackBatch(batch);
73
+ markFollowerAckBatchSucceeded(state, batch);
74
+ }
75
+ catch {
76
+ markFollowerAckBatchFailed(state, batch);
77
+ return;
78
+ }
79
+ }
80
+ }
81
+ function getFollowerShutdownAckIds(state) {
82
+ return [...state.deliveredAwaitingAckIds];
83
+ }
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.probeGitBranch = probeGitBranch;
37
+ exports.probeGitContext = probeGitContext;
38
+ exports.createGitContextCache = createGitContextCache;
39
+ const node_child_process_1 = require("node:child_process");
40
+ const path = __importStar(require("node:path"));
41
+ const node_util_1 = require("node:util");
42
+ const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
43
+ async function runGitCommand(args, cwd, runner) {
44
+ try {
45
+ const result = await runner("git", args, { cwd, encoding: "utf-8" });
46
+ const stdout = typeof result.stdout === "string" ? result.stdout : result.stdout?.toString();
47
+ const trimmed = stdout?.trim();
48
+ return trimmed ? trimmed : undefined;
49
+ }
50
+ catch {
51
+ return undefined;
52
+ }
53
+ }
54
+ async function probeGitBranch(cwd = process.cwd(), runner = execFileAsync) {
55
+ return runGitCommand(["branch", "--show-current"], cwd, runner);
56
+ }
57
+ async function probeGitContext(cwd = process.cwd(), runner = execFileAsync) {
58
+ const repoRoot = await runGitCommand(["rev-parse", "--show-toplevel"], cwd, runner);
59
+ const branch = await probeGitBranch(cwd, runner);
60
+ const resolvedRepoRoot = repoRoot ?? cwd;
61
+ return {
62
+ cwd,
63
+ repo: path.basename(resolvedRepoRoot),
64
+ repoRoot,
65
+ branch,
66
+ };
67
+ }
68
+ function createGitContextCache(loader) {
69
+ let cached = null;
70
+ let inflight = null;
71
+ return {
72
+ async get() {
73
+ if (cached)
74
+ return cached;
75
+ if (inflight)
76
+ return inflight;
77
+ inflight = loader()
78
+ .then((result) => {
79
+ cached = result;
80
+ return result;
81
+ })
82
+ .finally(() => {
83
+ inflight = null;
84
+ });
85
+ return inflight;
86
+ },
87
+ peek() {
88
+ return cached;
89
+ },
90
+ clear() {
91
+ cached = null;
92
+ inflight = null;
93
+ },
94
+ };
95
+ }