@pinet/slack-bridge 0.1.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/LICENSE +21 -0
- package/README.md +570 -0
- package/dist/activity-log.d.ts +62 -0
- package/dist/activity-log.js +293 -0
- package/dist/agent-completion-runtime.d.ts +17 -0
- package/dist/agent-completion-runtime.js +26 -0
- package/dist/agent-event-runtime.d.ts +13 -0
- package/dist/agent-event-runtime.js +27 -0
- package/dist/agent-prompt-guidance.d.ts +22 -0
- package/dist/agent-prompt-guidance.js +46 -0
- package/dist/broker/adapters/slack.d.ts +74 -0
- package/dist/broker/adapters/slack.js +563 -0
- package/dist/broker/adapters/types.d.ts +1 -0
- package/dist/broker/adapters/types.js +1 -0
- package/dist/broker/agent-messaging.d.ts +1 -0
- package/dist/broker/agent-messaging.js +1 -0
- package/dist/broker/auth.d.ts +1 -0
- package/dist/broker/auth.js +1 -0
- package/dist/broker/client.d.ts +167 -0
- package/dist/broker/client.js +624 -0
- package/dist/broker/control-plane-dashboard.d.ts +98 -0
- package/dist/broker/control-plane-dashboard.js +213 -0
- package/dist/broker/ghost-reaper.d.ts +49 -0
- package/dist/broker/ghost-reaper.js +209 -0
- package/dist/broker/index.d.ts +36 -0
- package/dist/broker/index.js +90 -0
- package/dist/broker/leader.d.ts +1 -0
- package/dist/broker/leader.js +1 -0
- package/dist/broker/maintenance.d.ts +1 -0
- package/dist/broker/maintenance.js +1 -0
- package/dist/broker/message-send.d.ts +1 -0
- package/dist/broker/message-send.js +1 -0
- package/dist/broker/paths.d.ts +1 -0
- package/dist/broker/paths.js +1 -0
- package/dist/broker/raw-tcp-loopback.d.ts +1 -0
- package/dist/broker/raw-tcp-loopback.js +1 -0
- package/dist/broker/router.d.ts +1 -0
- package/dist/broker/router.js +1 -0
- package/dist/broker/schema.d.ts +9 -0
- package/dist/broker/schema.js +63 -0
- package/dist/broker/socket-server.d.ts +120 -0
- package/dist/broker/socket-server.js +1087 -0
- package/dist/broker/types.d.ts +1 -0
- package/dist/broker/types.js +1 -0
- package/dist/broker-delivery.d.ts +10 -0
- package/dist/broker-delivery.js +26 -0
- package/dist/broker-inbound-persistence.d.ts +11 -0
- package/dist/broker-inbound-persistence.js +22 -0
- package/dist/broker-prompt-loader.d.ts +34 -0
- package/dist/broker-prompt-loader.js +185 -0
- package/dist/broker-runtime-access.d.ts +14 -0
- package/dist/broker-runtime-access.js +20 -0
- package/dist/broker-runtime.d.ts +100 -0
- package/dist/broker-runtime.js +533 -0
- package/dist/broker-thread-owner-hints.d.ts +10 -0
- package/dist/broker-thread-owner-hints.js +14 -0
- package/dist/canvases.d.ts +80 -0
- package/dist/canvases.js +221 -0
- package/dist/command-registration-runtime.d.ts +9 -0
- package/dist/command-registration-runtime.js +9 -0
- package/dist/core-tool-guardrails.d.ts +24 -0
- package/dist/core-tool-guardrails.js +66 -0
- package/dist/deploy-manifest.d.ts +27 -0
- package/dist/deploy-manifest.js +197 -0
- package/dist/follower-delivery.d.ts +16 -0
- package/dist/follower-delivery.js +70 -0
- package/dist/follower-runtime.d.ts +61 -0
- package/dist/follower-runtime.js +350 -0
- package/dist/git-metadata.d.ts +35 -0
- package/dist/git-metadata.js +88 -0
- package/dist/guardrails.d.ts +69 -0
- package/dist/guardrails.js +283 -0
- package/dist/helpers.d.ts +550 -0
- package/dist/helpers.js +2711 -0
- package/dist/home-tab.d.ts +27 -0
- package/dist/home-tab.js +242 -0
- package/dist/imessage-tools.d.ts +33 -0
- package/dist/imessage-tools.js +96 -0
- package/dist/inbox-drain-runtime.d.ts +30 -0
- package/dist/inbox-drain-runtime.js +71 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1330 -0
- package/dist/persisted-runtime-state.d.ts +52 -0
- package/dist/persisted-runtime-state.js +98 -0
- package/dist/pinet-activity-formatting.d.ts +20 -0
- package/dist/pinet-activity-formatting.js +43 -0
- package/dist/pinet-agent-status.d.ts +36 -0
- package/dist/pinet-agent-status.js +48 -0
- package/dist/pinet-commands.d.ts +70 -0
- package/dist/pinet-commands.js +402 -0
- package/dist/pinet-confirmation-replies.d.ts +7 -0
- package/dist/pinet-confirmation-replies.js +30 -0
- package/dist/pinet-control-plane-dashboard.d.ts +34 -0
- package/dist/pinet-control-plane-dashboard.js +86 -0
- package/dist/pinet-home-tabs.d.ts +36 -0
- package/dist/pinet-home-tabs.js +101 -0
- package/dist/pinet-maintenance-delivery.d.ts +22 -0
- package/dist/pinet-maintenance-delivery.js +45 -0
- package/dist/pinet-mesh-ops.d.ts +97 -0
- package/dist/pinet-mesh-ops.js +266 -0
- package/dist/pinet-registration-gate.d.ts +14 -0
- package/dist/pinet-registration-gate.js +42 -0
- package/dist/pinet-remote-control-acks.d.ts +16 -0
- package/dist/pinet-remote-control-acks.js +46 -0
- package/dist/pinet-remote-control.d.ts +17 -0
- package/dist/pinet-remote-control.js +74 -0
- package/dist/pinet-runtime-composition.d.ts +27 -0
- package/dist/pinet-runtime-composition.js +20 -0
- package/dist/pinet-tools.d.ts +67 -0
- package/dist/pinet-tools.js +1211 -0
- package/dist/prompts/broker/default.md +41 -0
- package/dist/prompts/broker/tmux.md +49 -0
- package/dist/ralph-loop.d.ts +76 -0
- package/dist/ralph-loop.js +524 -0
- package/dist/reaction-triggers.d.ts +25 -0
- package/dist/reaction-triggers.js +171 -0
- package/dist/repo-tool-guardrails.d.ts +20 -0
- package/dist/repo-tool-guardrails.js +81 -0
- package/dist/runtime-agent-context.d.ts +93 -0
- package/dist/runtime-agent-context.js +302 -0
- package/dist/runtime-mode.d.ts +10 -0
- package/dist/runtime-mode.js +39 -0
- package/dist/scheduled-wakeups.d.ts +1 -0
- package/dist/scheduled-wakeups.js +1 -0
- package/dist/session-ui-runtime.d.ts +20 -0
- package/dist/session-ui-runtime.js +163 -0
- package/dist/single-player-runtime.d.ts +71 -0
- package/dist/single-player-runtime.js +436 -0
- package/dist/skins/cosmere.json +1571 -0
- package/dist/skins/foundation.json +1304 -0
- package/dist/slack-access.d.ts +186 -0
- package/dist/slack-access.js +462 -0
- package/dist/slack-api.d.ts +2 -0
- package/dist/slack-api.js +1 -0
- package/dist/slack-block-kit.d.ts +36 -0
- package/dist/slack-block-kit.js +242 -0
- package/dist/slack-export.d.ts +31 -0
- package/dist/slack-export.js +206 -0
- package/dist/slack-message-context.d.ts +15 -0
- package/dist/slack-message-context.js +207 -0
- package/dist/slack-modals.d.ts +13 -0
- package/dist/slack-modals.js +79 -0
- package/dist/slack-pinet-runtime-adapter.d.ts +16 -0
- package/dist/slack-pinet-runtime-adapter.js +72 -0
- package/dist/slack-presence.d.ts +36 -0
- package/dist/slack-presence.js +89 -0
- package/dist/slack-request-runtime.d.ts +8 -0
- package/dist/slack-request-runtime.js +19 -0
- package/dist/slack-runtime-access.d.ts +28 -0
- package/dist/slack-runtime-access.js +107 -0
- package/dist/slack-scope-diagnostics.d.ts +31 -0
- package/dist/slack-scope-diagnostics.js +235 -0
- package/dist/slack-socket-dedup.d.ts +7 -0
- package/dist/slack-socket-dedup.js +135 -0
- package/dist/slack-thread-status.d.ts +41 -0
- package/dist/slack-thread-status.js +156 -0
- package/dist/slack-tool-policy-runtime.d.ts +36 -0
- package/dist/slack-tool-policy-runtime.js +96 -0
- package/dist/slack-tools.d.ts +52 -0
- package/dist/slack-tools.js +2688 -0
- package/dist/slack-turn-guardrails.d.ts +19 -0
- package/dist/slack-turn-guardrails.js +36 -0
- package/dist/slack-upload.d.ts +49 -0
- package/dist/slack-upload.js +224 -0
- package/dist/task-assignments.d.ts +45 -0
- package/dist/task-assignments.js +527 -0
- package/dist/thread-confirmations.d.ts +22 -0
- package/dist/thread-confirmations.js +139 -0
- package/dist/tool-registration-runtime.d.ts +13 -0
- package/dist/tool-registration-runtime.js +13 -0
- package/dist/ttl-cache.d.ts +47 -0
- package/dist/ttl-cache.js +105 -0
- package/manifest.yaml +57 -0
- package/package.json +60 -0
- package/skills/pinet-skin-creator/SKILL.md +109 -0
- package/skills/pinet-skin-creator/references/descriptor-format.md +99 -0
- package/skills/pinet-skin-creator/references/safety-checklist.md +56 -0
- package/skills/pinet-skin-creator/templates/pinet-skin-descriptor.json +77 -0
- package/skills/slack-bridge/SKILL.md +360 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { InboxMessage } from "./helpers.js";
|
|
2
|
+
type SlackTurnMessageRef = Pick<InboxMessage, "threadTs"> & Partial<Pick<InboxMessage, "channel">>;
|
|
3
|
+
export interface PendingSlackToolPolicyTurn {
|
|
4
|
+
prompt: string;
|
|
5
|
+
threadTs: string | undefined;
|
|
6
|
+
channel: string | undefined;
|
|
7
|
+
threadCount: number;
|
|
8
|
+
}
|
|
9
|
+
export declare function buildPendingSlackToolPolicyTurn(prompt: string, messages: SlackTurnMessageRef[]): PendingSlackToolPolicyTurn;
|
|
10
|
+
export declare function enqueuePendingSlackToolPolicyTurn(queue: PendingSlackToolPolicyTurn[], prompt: string, messages: SlackTurnMessageRef[]): PendingSlackToolPolicyTurn;
|
|
11
|
+
export declare function consumePendingSlackToolPolicyTurn(queue: PendingSlackToolPolicyTurn[], text: string): PendingSlackToolPolicyTurn | null;
|
|
12
|
+
export declare function removePendingSlackToolPolicyTurn(queue: PendingSlackToolPolicyTurn[], entry: PendingSlackToolPolicyTurn): void;
|
|
13
|
+
export declare function deliverTrackedSlackFollowUpMessage(options: {
|
|
14
|
+
queue: PendingSlackToolPolicyTurn[];
|
|
15
|
+
prompt: string;
|
|
16
|
+
messages: SlackTurnMessageRef[];
|
|
17
|
+
deliver: (prompt: string) => boolean;
|
|
18
|
+
}): boolean;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export function buildPendingSlackToolPolicyTurn(prompt, messages) {
|
|
2
|
+
const threadIds = [...new Set(messages.map((message) => message.threadTs).filter(Boolean))];
|
|
3
|
+
const channels = [...new Set(messages.map((message) => message.channel).filter(Boolean))];
|
|
4
|
+
return {
|
|
5
|
+
prompt,
|
|
6
|
+
threadTs: threadIds.length === 1 ? threadIds[0] : undefined,
|
|
7
|
+
channel: channels.length === 1 ? channels[0] : undefined,
|
|
8
|
+
threadCount: threadIds.length,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export function enqueuePendingSlackToolPolicyTurn(queue, prompt, messages) {
|
|
12
|
+
const entry = buildPendingSlackToolPolicyTurn(prompt, messages);
|
|
13
|
+
queue.push(entry);
|
|
14
|
+
return entry;
|
|
15
|
+
}
|
|
16
|
+
export function consumePendingSlackToolPolicyTurn(queue, text) {
|
|
17
|
+
const index = queue.findIndex((entry) => entry.prompt === text);
|
|
18
|
+
if (index < 0)
|
|
19
|
+
return null;
|
|
20
|
+
const [entry] = queue.splice(index, 1);
|
|
21
|
+
return entry ?? null;
|
|
22
|
+
}
|
|
23
|
+
export function removePendingSlackToolPolicyTurn(queue, entry) {
|
|
24
|
+
const index = queue.indexOf(entry);
|
|
25
|
+
if (index >= 0) {
|
|
26
|
+
queue.splice(index, 1);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export function deliverTrackedSlackFollowUpMessage(options) {
|
|
30
|
+
const entry = enqueuePendingSlackToolPolicyTurn(options.queue, options.prompt, options.messages);
|
|
31
|
+
if (options.deliver(options.prompt)) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
removePendingSlackToolPolicyTurn(options.queue, entry);
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { readFile, realpath, stat } from "node:fs/promises";
|
|
2
|
+
import type { SlackResult } from "./slack-api.js";
|
|
3
|
+
export interface SlackUploadParams {
|
|
4
|
+
content?: string;
|
|
5
|
+
path?: string;
|
|
6
|
+
filename?: string;
|
|
7
|
+
filetype?: string;
|
|
8
|
+
title?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface PreparedSlackUpload {
|
|
11
|
+
bytes: Buffer;
|
|
12
|
+
byteLength: number;
|
|
13
|
+
filename: string;
|
|
14
|
+
title: string;
|
|
15
|
+
filetype?: string;
|
|
16
|
+
snippetType?: string;
|
|
17
|
+
source: "content" | "path";
|
|
18
|
+
resolvedPath?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface SlackUploadDeps {
|
|
21
|
+
slack: (method: string, token: string, body?: Record<string, unknown>) => Promise<SlackResult>;
|
|
22
|
+
token: string;
|
|
23
|
+
fetchImpl?: (url: string, init: RequestInit) => Promise<Pick<Response, "ok" | "status" | "statusText" | "text">>;
|
|
24
|
+
}
|
|
25
|
+
export interface PerformSlackUploadOptions extends SlackUploadDeps {
|
|
26
|
+
upload: PreparedSlackUpload;
|
|
27
|
+
channelId: string;
|
|
28
|
+
threadTs?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface CompletedSlackUpload {
|
|
31
|
+
fileId: string;
|
|
32
|
+
response: SlackResult;
|
|
33
|
+
}
|
|
34
|
+
interface PrepareSlackUploadFs {
|
|
35
|
+
readFileImpl?: typeof readFile;
|
|
36
|
+
realpathImpl?: typeof realpath;
|
|
37
|
+
statImpl?: typeof stat;
|
|
38
|
+
}
|
|
39
|
+
export declare function inferSlackUploadFiletype(filename: string | undefined, explicitFiletype?: string): string | undefined;
|
|
40
|
+
export declare function chooseSlackSnippetType(upload: {
|
|
41
|
+
source: "content" | "path";
|
|
42
|
+
byteLength: number;
|
|
43
|
+
filename: string;
|
|
44
|
+
filetype?: string;
|
|
45
|
+
}): string | undefined;
|
|
46
|
+
export declare function resolveSlackUploadPath(inputPath: string, cwd: string, tmpdir: string, fsDeps?: PrepareSlackUploadFs): Promise<string>;
|
|
47
|
+
export declare function prepareSlackUpload(params: SlackUploadParams, cwd: string, tmpdir: string, fsDeps?: PrepareSlackUploadFs): Promise<PreparedSlackUpload>;
|
|
48
|
+
export declare function performSlackUpload({ upload, channelId, threadTs, slack, token, fetchImpl, }: PerformSlackUploadOptions): Promise<CompletedSlackUpload>;
|
|
49
|
+
export {};
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { readFile, realpath, stat } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
const FILETYPE_ALIASES = {
|
|
4
|
+
cjs: "javascript",
|
|
5
|
+
htm: "html",
|
|
6
|
+
js: "javascript",
|
|
7
|
+
jsx: "javascript",
|
|
8
|
+
mjs: "javascript",
|
|
9
|
+
md: "markdown",
|
|
10
|
+
py: "python",
|
|
11
|
+
rb: "ruby",
|
|
12
|
+
sh: "shell",
|
|
13
|
+
ts: "typescript",
|
|
14
|
+
tsx: "typescript",
|
|
15
|
+
yml: "yaml",
|
|
16
|
+
zsh: "shell",
|
|
17
|
+
};
|
|
18
|
+
const DEFAULT_SNIPPET_TYPE = "text";
|
|
19
|
+
const MAX_SNIPPET_BYTES = 1_000_000;
|
|
20
|
+
export function inferSlackUploadFiletype(filename, explicitFiletype) {
|
|
21
|
+
const raw = (explicitFiletype ?? path.extname(filename ?? "").slice(1)).trim().toLowerCase();
|
|
22
|
+
if (!raw)
|
|
23
|
+
return undefined;
|
|
24
|
+
return FILETYPE_ALIASES[raw] ?? raw;
|
|
25
|
+
}
|
|
26
|
+
export function chooseSlackSnippetType(upload) {
|
|
27
|
+
if (upload.source !== "content")
|
|
28
|
+
return undefined;
|
|
29
|
+
if (upload.byteLength > MAX_SNIPPET_BYTES)
|
|
30
|
+
return undefined;
|
|
31
|
+
return normalizeSnippetType(inferSlackUploadFiletype(upload.filename, upload.filetype));
|
|
32
|
+
}
|
|
33
|
+
function isWithinRoot(candidate, root) {
|
|
34
|
+
return candidate === root || candidate.startsWith(`${root}${path.sep}`);
|
|
35
|
+
}
|
|
36
|
+
function normalizeSnippetType(filetype) {
|
|
37
|
+
return filetype?.trim().toLowerCase() || DEFAULT_SNIPPET_TYPE;
|
|
38
|
+
}
|
|
39
|
+
function buildUploadMetadataPayload(upload, includeSnippetType) {
|
|
40
|
+
return {
|
|
41
|
+
filename: upload.filename,
|
|
42
|
+
length: upload.byteLength,
|
|
43
|
+
...(includeSnippetType && upload.snippetType ? { snippet_type: upload.snippetType } : {}),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function isInvalidUploadMetadataError(error) {
|
|
47
|
+
if (!(error instanceof Error)) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
const lower = error.message.toLowerCase();
|
|
51
|
+
return lower.includes("files.getuploadurlexternal") && lower.includes("invalid_arguments");
|
|
52
|
+
}
|
|
53
|
+
function formatUploadMetadataRetryError(upload, retryError, originalError) {
|
|
54
|
+
return new Error(`Slack files.getUploadURLExternal: invalid_arguments after retry without snippet_type; Slack rejected upload metadata for filename=${JSON.stringify(upload.filename)} byte_length=${upload.byteLength}. retry_error=${formatUploadError(retryError)}; original_error=${formatUploadError(originalError)}`);
|
|
55
|
+
}
|
|
56
|
+
function getUploadHost(uploadUrl) {
|
|
57
|
+
try {
|
|
58
|
+
return new URL(uploadUrl).hostname || "<unknown>";
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return "<unknown>";
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function formatUploadError(error) {
|
|
65
|
+
if (error instanceof Error) {
|
|
66
|
+
if (typeof error.cause === "string") {
|
|
67
|
+
return `${error.message}; cause: ${error.cause}`;
|
|
68
|
+
}
|
|
69
|
+
if (error.cause instanceof Error) {
|
|
70
|
+
const cause = [
|
|
71
|
+
error.cause.message,
|
|
72
|
+
error.cause.code,
|
|
73
|
+
error.cause.name,
|
|
74
|
+
]
|
|
75
|
+
.filter((value) => typeof value === "string" && value.length > 0)
|
|
76
|
+
.join("; ");
|
|
77
|
+
return cause ? `${error.message}; cause: ${cause}` : error.message;
|
|
78
|
+
}
|
|
79
|
+
return error.message;
|
|
80
|
+
}
|
|
81
|
+
return String(error);
|
|
82
|
+
}
|
|
83
|
+
function isLikelyProxyOrFirewallFailure(responseText, headers) {
|
|
84
|
+
const headerText = [
|
|
85
|
+
headers?.get("x-proxy-error"),
|
|
86
|
+
headers?.get("x-proxy-status"),
|
|
87
|
+
headers?.get("via"),
|
|
88
|
+
headers?.get("server"),
|
|
89
|
+
]
|
|
90
|
+
.filter((value) => typeof value === "string")
|
|
91
|
+
.join(" ");
|
|
92
|
+
const combined = `${responseText} ${headerText}`.toLowerCase();
|
|
93
|
+
return /(blocked-by-allowlist|possible outbound proxy|blocked by proxy|proxy-firewall|firewall)/.test(combined);
|
|
94
|
+
}
|
|
95
|
+
export async function resolveSlackUploadPath(inputPath, cwd, tmpdir, fsDeps = {}) {
|
|
96
|
+
const { realpathImpl = realpath, statImpl = stat } = fsDeps;
|
|
97
|
+
const requestedPath = inputPath.trim();
|
|
98
|
+
if (!requestedPath) {
|
|
99
|
+
throw new Error("path is required when uploading from a local file.");
|
|
100
|
+
}
|
|
101
|
+
const candidate = path.isAbsolute(requestedPath)
|
|
102
|
+
? requestedPath
|
|
103
|
+
: path.resolve(cwd, requestedPath);
|
|
104
|
+
const [resolvedCandidate, resolvedCwd, resolvedTmpdir] = await Promise.all([
|
|
105
|
+
realpathImpl(candidate),
|
|
106
|
+
realpathImpl(cwd),
|
|
107
|
+
realpathImpl(tmpdir),
|
|
108
|
+
]);
|
|
109
|
+
if (!isWithinRoot(resolvedCandidate, resolvedCwd) &&
|
|
110
|
+
!isWithinRoot(resolvedCandidate, resolvedTmpdir)) {
|
|
111
|
+
throw new Error("For safety, the slack dispatcher upload action only allows local file paths inside the current working directory or the system temp directory. For other files, read the content explicitly and upload it via the content parameter.");
|
|
112
|
+
}
|
|
113
|
+
const fileStats = await statImpl(resolvedCandidate);
|
|
114
|
+
if (!fileStats.isFile()) {
|
|
115
|
+
throw new Error(`Local upload path is not a file: ${requestedPath}`);
|
|
116
|
+
}
|
|
117
|
+
return resolvedCandidate;
|
|
118
|
+
}
|
|
119
|
+
export async function prepareSlackUpload(params, cwd, tmpdir, fsDeps = {}) {
|
|
120
|
+
const { readFileImpl = readFile } = fsDeps;
|
|
121
|
+
const hasContent = typeof params.content === "string";
|
|
122
|
+
const hasPath = typeof params.path === "string" && params.path.trim().length > 0;
|
|
123
|
+
if (hasContent === hasPath) {
|
|
124
|
+
throw new Error("Provide exactly one of content or path.");
|
|
125
|
+
}
|
|
126
|
+
let bytes;
|
|
127
|
+
let filename = params.filename?.trim();
|
|
128
|
+
let resolvedPath;
|
|
129
|
+
let source;
|
|
130
|
+
if (hasContent) {
|
|
131
|
+
if (!filename) {
|
|
132
|
+
throw new Error("filename is required when uploading inline content.");
|
|
133
|
+
}
|
|
134
|
+
bytes = Buffer.from(params.content ?? "", "utf8");
|
|
135
|
+
source = "content";
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
resolvedPath = await resolveSlackUploadPath(params.path, cwd, tmpdir, fsDeps);
|
|
139
|
+
bytes = await readFileImpl(resolvedPath);
|
|
140
|
+
filename = filename || path.basename(resolvedPath);
|
|
141
|
+
source = "path";
|
|
142
|
+
}
|
|
143
|
+
const sanitizedFilename = filename?.trim();
|
|
144
|
+
if (!sanitizedFilename) {
|
|
145
|
+
throw new Error("filename is required.");
|
|
146
|
+
}
|
|
147
|
+
const filetype = inferSlackUploadFiletype(sanitizedFilename, params.filetype);
|
|
148
|
+
const title = params.title?.trim() || sanitizedFilename;
|
|
149
|
+
return {
|
|
150
|
+
bytes,
|
|
151
|
+
byteLength: bytes.byteLength,
|
|
152
|
+
filename: sanitizedFilename,
|
|
153
|
+
title,
|
|
154
|
+
filetype,
|
|
155
|
+
snippetType: chooseSlackSnippetType({
|
|
156
|
+
source,
|
|
157
|
+
byteLength: bytes.byteLength,
|
|
158
|
+
filename: sanitizedFilename,
|
|
159
|
+
filetype,
|
|
160
|
+
}),
|
|
161
|
+
source,
|
|
162
|
+
...(resolvedPath ? { resolvedPath } : {}),
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
export async function performSlackUpload({ upload, channelId, threadTs, slack, token, fetchImpl = fetch, }) {
|
|
166
|
+
let getUploadResponse;
|
|
167
|
+
try {
|
|
168
|
+
getUploadResponse = await slack("files.getUploadURLExternal", token, buildUploadMetadataPayload(upload, true));
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
if (upload.snippetType && isInvalidUploadMetadataError(error)) {
|
|
172
|
+
try {
|
|
173
|
+
getUploadResponse = await slack("files.getUploadURLExternal", token, buildUploadMetadataPayload(upload, false));
|
|
174
|
+
}
|
|
175
|
+
catch (retryError) {
|
|
176
|
+
if (isInvalidUploadMetadataError(retryError)) {
|
|
177
|
+
throw formatUploadMetadataRetryError(upload, retryError, error);
|
|
178
|
+
}
|
|
179
|
+
throw retryError;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
throw error;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
const uploadUrl = typeof getUploadResponse.upload_url === "string" ? getUploadResponse.upload_url : null;
|
|
187
|
+
const uploadHost = uploadUrl ? getUploadHost(uploadUrl) : "<unknown>";
|
|
188
|
+
const fileId = typeof getUploadResponse.file_id === "string" ? getUploadResponse.file_id : null;
|
|
189
|
+
if (!uploadUrl || !fileId) {
|
|
190
|
+
throw new Error("Slack files.getUploadURLExternal did not return an upload URL and file ID.");
|
|
191
|
+
}
|
|
192
|
+
const rawBody = new Uint8Array(upload.bytes.byteLength);
|
|
193
|
+
rawBody.set(upload.bytes);
|
|
194
|
+
let rawUploadResponse;
|
|
195
|
+
try {
|
|
196
|
+
rawUploadResponse = await fetchImpl(uploadUrl, {
|
|
197
|
+
method: "POST",
|
|
198
|
+
headers: {
|
|
199
|
+
"Content-Length": String(upload.byteLength),
|
|
200
|
+
"Content-Type": upload.source === "content" ? "text/plain; charset=utf-8" : "application/octet-stream",
|
|
201
|
+
},
|
|
202
|
+
body: new Blob([rawBody]),
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
throw new Error(`Slack raw upload failed (transport): ${formatUploadError(error)}; host=${uploadHost}; filename=${upload.filename} byte_length=${upload.byteLength}`);
|
|
207
|
+
}
|
|
208
|
+
if (!rawUploadResponse.ok) {
|
|
209
|
+
const details = (await rawUploadResponse.text()).trim();
|
|
210
|
+
const statusText = rawUploadResponse.statusText;
|
|
211
|
+
const statusTextForHeader = statusText ? ` ${statusText}` : "";
|
|
212
|
+
const withDetails = details.length > 0 ? `${statusText ? `${statusText}: ` : ""}${details}` : "";
|
|
213
|
+
const rawUploadResponseHeaders = rawUploadResponse.headers instanceof Headers ? rawUploadResponse.headers : undefined;
|
|
214
|
+
const isProxyOrFirewallFailure = isLikelyProxyOrFirewallFailure(details, rawUploadResponseHeaders);
|
|
215
|
+
const hint = isProxyOrFirewallFailure ? " [possible outbound proxy/firewall block]" : "";
|
|
216
|
+
throw new Error(`Slack raw upload failed (HTTP ${rawUploadResponse.status}${statusTextForHeader})${withDetails ? ` ${withDetails}` : ""}${hint}; host=${uploadHost}; filename=${upload.filename} byte_length=${upload.byteLength}`);
|
|
217
|
+
}
|
|
218
|
+
const response = await slack("files.completeUploadExternal", token, {
|
|
219
|
+
files: [{ id: fileId, title: upload.title }],
|
|
220
|
+
channel_id: channelId,
|
|
221
|
+
...(threadTs ? { thread_ts: threadTs } : {}),
|
|
222
|
+
});
|
|
223
|
+
return { fileId, response };
|
|
224
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { AgentInfo, TaskAssignmentInfo, TaskAssignmentKind, TaskAssignmentStatus } from "./broker/types.js";
|
|
2
|
+
export type CommandResult = {
|
|
3
|
+
stdout?: string | Buffer;
|
|
4
|
+
};
|
|
5
|
+
export type CommandRunner = (file: string, args: string[], options: {
|
|
6
|
+
cwd: string;
|
|
7
|
+
encoding: "utf-8";
|
|
8
|
+
}) => Promise<CommandResult>;
|
|
9
|
+
export interface ParsedTaskAssignment {
|
|
10
|
+
issueNumber: number;
|
|
11
|
+
branch: string | null;
|
|
12
|
+
repoOwner: string | null;
|
|
13
|
+
repoName: string | null;
|
|
14
|
+
taskKind: TaskAssignmentKind;
|
|
15
|
+
}
|
|
16
|
+
export interface PullRequestSnapshot {
|
|
17
|
+
number: number;
|
|
18
|
+
state: string;
|
|
19
|
+
mergedAt: string | null;
|
|
20
|
+
headRefName: string;
|
|
21
|
+
}
|
|
22
|
+
export interface IssueSnapshot {
|
|
23
|
+
number: number;
|
|
24
|
+
state: string;
|
|
25
|
+
}
|
|
26
|
+
export interface ResolvedTaskAssignment extends TaskAssignmentInfo {
|
|
27
|
+
nextStatus: TaskAssignmentStatus;
|
|
28
|
+
nextPrNumber: number | null;
|
|
29
|
+
branchAheadCount: number;
|
|
30
|
+
issueState: "OPEN" | "CLOSED" | null;
|
|
31
|
+
}
|
|
32
|
+
export declare function extractTaskAssignmentsFromMessage(message: string): ParsedTaskAssignment[];
|
|
33
|
+
export declare function normalizeTrackedTaskAssignments(assignments: TaskAssignmentInfo[], sourceMessagesById?: ReadonlyMap<number, string>): TaskAssignmentInfo[];
|
|
34
|
+
export declare function resolveTaskAssignments(assignments: TaskAssignmentInfo[], cwd?: string, runner?: CommandRunner): Promise<ResolvedTaskAssignment[]>;
|
|
35
|
+
export declare function hasTaskAssignmentStatusChange(assignment: ResolvedTaskAssignment): boolean;
|
|
36
|
+
type ReportableTaskAssignment = Pick<TaskAssignmentInfo, "agentId" | "issueNumber" | "branch" | "status" | "prNumber" | "taskKind" | "repoOwner" | "repoName" | "repoRoot"> & {
|
|
37
|
+
issueState?: ResolvedTaskAssignment["issueState"];
|
|
38
|
+
};
|
|
39
|
+
export declare function buildTaskAssignmentReport(assignments: ReportableTaskAssignment[], agentsById: ReadonlyMap<string, Pick<AgentInfo, "emoji" | "name">>, cycleStartedAt?: string): string | null;
|
|
40
|
+
export interface PendingTaskAssignmentReport {
|
|
41
|
+
message: string;
|
|
42
|
+
signature: string;
|
|
43
|
+
}
|
|
44
|
+
export declare function getPendingTaskAssignmentReport(assignments: ReportableTaskAssignment[], agentsById: ReadonlyMap<string, Pick<AgentInfo, "emoji" | "name">>, lastDeliveredSignature: string, cycleStartedAt?: string): PendingTaskAssignmentReport | null;
|
|
45
|
+
export {};
|