@kamilio/wire 0.1.12 → 0.1.14

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 (35) hide show
  1. package/README.md +13 -0
  2. package/dist/adapters/root.d.ts +2 -2
  3. package/dist/adapters/root.d.ts.map +1 -1
  4. package/dist/adapters/root.js +25 -6
  5. package/dist/adapters/root.js.map +1 -1
  6. package/dist/cli.d.ts.map +1 -1
  7. package/dist/cli.js +9 -5
  8. package/dist/cli.js.map +1 -1
  9. package/dist/executable.d.ts.map +1 -1
  10. package/dist/executable.js +3 -2
  11. package/dist/executable.js.map +1 -1
  12. package/node_modules/provider-notion/dist/index.d.ts +1 -0
  13. package/node_modules/provider-notion/dist/index.d.ts.map +1 -1
  14. package/node_modules/provider-notion/dist/index.js +1 -0
  15. package/node_modules/provider-notion/dist/index.js.map +1 -1
  16. package/node_modules/provider-notion/dist/notion-sync.d.ts +3 -1
  17. package/node_modules/provider-notion/dist/notion-sync.d.ts.map +1 -1
  18. package/node_modules/provider-notion/dist/notion-sync.js +99 -26
  19. package/node_modules/provider-notion/dist/notion-sync.js.map +1 -1
  20. package/node_modules/provider-notion/src/index.ts +2 -0
  21. package/node_modules/provider-notion/src/notion-sync.ts +86 -25
  22. package/node_modules/provider-slack/dist/index.d.ts +1 -1
  23. package/node_modules/provider-slack/dist/index.d.ts.map +1 -1
  24. package/node_modules/provider-slack/dist/index.js +1 -1
  25. package/node_modules/provider-slack/dist/index.js.map +1 -1
  26. package/node_modules/provider-slack/dist/slack.d.ts +3 -0
  27. package/node_modules/provider-slack/dist/slack.d.ts.map +1 -1
  28. package/node_modules/provider-slack/dist/slack.js +111 -20
  29. package/node_modules/provider-slack/dist/slack.js.map +1 -1
  30. package/node_modules/provider-slack/src/index.ts +1 -1
  31. package/node_modules/provider-slack/src/slack.ts +101 -19
  32. package/package.json +1 -1
  33. package/src/adapters/root.ts +26 -7
  34. package/src/cli.ts +5 -3
  35. package/src/executable.ts +4 -2
@@ -1,6 +1,6 @@
1
1
  import { slackService } from "./slack.js";
2
2
 
3
- export { slackService, markdownToSlackBlocks, slackBlocksToMarkdown, sendSlackMessage, type SlackSentMessage } from "./slack.js";
3
+ export { slackService, markdownToSlackBlocks, slackBlocksToMarkdown, sendSlackMessage, editSlackMessage, lookupSlackUsers, type SlackSentMessage } from "./slack.js";
4
4
  export const slackProvider = Object.freeze({
5
5
  services: [slackService] as const,
6
6
  });
@@ -42,6 +42,7 @@ function inlinePlain(nodes: readonly MdNode[]): string {
42
42
  if ("children" in node) return inlinePlain(node.children);
43
43
  if (node.type === "image") return node.alt;
44
44
  if (node.type === "break") return "\n";
45
+ if (node.type === "thematicBreak") return "──────────";
45
46
  if (node.type === "footnoteReference") return `[${node.label}]`;
46
47
  throw new Error(`Unsupported Slack inline node: ${node.type}`);
47
48
  }).join("");
@@ -263,14 +264,90 @@ async function slackSession(runtime: RuntimeCapabilities) {
263
264
  return { origin: metadata["origin"]!, token: metadata["token"]!, cookie: cookies.map((value) => `${value.name}=${value.value}`).join("; ") };
264
265
  }
265
266
 
266
- export type SlackSentMessage = Readonly<{ channel: string; ts: string; url: string; thread_ts?: string }>;
267
+ export async function lookupSlackUsers(runtime: RuntimeCapabilities, query: string): Promise<readonly JsonObject[]> {
268
+ query = query.trim();
269
+ if (query === "") throw new Error("Slack user lookup requires an email or name");
270
+ const session = await slackSession(runtime);
271
+ const request = (method: string, parameters: Readonly<Record<string, string>>) => api(runtime, session.origin, session.token, session.cookie, method, parameters);
272
+ const email = /^[^@\s]+@[^@\s]+$/.test(query);
273
+ const found: JsonObject[] = [];
274
+ let cursor: string | undefined;
275
+ do {
276
+ const response = await request("users.list", { limit: "200", ...(cursor === undefined ? {} : { cursor }) });
277
+ for (const member of response["members"] as readonly JsonObject[]) {
278
+ if (member["deleted"] === true) continue;
279
+ const profile = member["profile"] as JsonObject | undefined;
280
+ const names = [member["name"], member["real_name"], profile?.["display_name"], profile?.["real_name"], profile?.["email"]].filter((value): value is string => typeof value === "string");
281
+ if (email ? (profile?.["email"] as string | undefined)?.toLowerCase() === query.toLowerCase() : query.startsWith("@") ? member["name"] === query.slice(1) : query.toLowerCase().split(/\s+/).every((word) => names.some((name) => name.toLowerCase().includes(word)))) found.push(member);
282
+ }
283
+ cursor = (response["response_metadata"] as JsonObject | undefined)?.["next_cursor"] as string | undefined;
284
+ } while (cursor !== undefined && cursor !== "");
285
+ return found;
286
+ }
287
+
288
+ function slackMessageParts(markdown: string): string[] {
289
+ const ast = parse(markdown).ast;
290
+ if (ast.type !== "root") throw new Error("Expected Markdown document");
291
+ const bytes = Buffer.from(markdown);
292
+ const parts: string[] = [];
293
+ let start = 0;
294
+ for (const node of ast.children) {
295
+ if (node.type !== "thematicBreak" || bytes.subarray(node.range!.start, node.range!.end).toString().trim() !== "---") continue;
296
+ parts.push(bytes.subarray(start, node.range!.start).toString().trim());
297
+ start = node.range!.end;
298
+ }
299
+ parts.push(bytes.subarray(start).toString().trim());
300
+ if (parts.some((part) => part === "")) throw new Error("Slack thread sections must not be empty");
301
+ return parts;
302
+ }
303
+
304
+ async function resolveSlackEmailMentions(runtime: RuntimeCapabilities, blocks: readonly JsonObject[], users: Map<string, string>): Promise<readonly JsonObject[]> {
305
+ const resolve = async (element: JsonObject): Promise<JsonObject> => {
306
+ if (element["type"] === "link" && typeof element["text"] === "string" && element["text"].startsWith("@") && typeof element["url"] === "string" && element["url"].startsWith("mailto:")) {
307
+ const email = decodeURIComponent(element["url"].slice(7));
308
+ if (!/^[^@\s?]+@[^@\s?]+$/.test(email)) throw new Error(`Invalid mention email: ${email}`);
309
+ const key = email.toLowerCase();
310
+ if (!users.has(key)) {
311
+ const matches = await lookupSlackUsers(runtime, email);
312
+ if (matches.length !== 1) throw new Error(`Expected one Slack user for ${email}, found ${matches.length}`);
313
+ users.set(key, matches[0]!["id"] as string);
314
+ }
315
+ return { type: "user", user_id: users.get(key)!, ...(element["style"] === undefined ? {} : { style: element["style"] }) };
316
+ }
317
+ if (Array.isArray(element["elements"])) {
318
+ const elements: JsonObject[] = [];
319
+ for (const child of element["elements"] as readonly JsonObject[]) elements.push(await resolve(child));
320
+ return { ...element, elements };
321
+ }
322
+ return element;
323
+ };
324
+ const result: JsonObject[] = [];
325
+ for (const block of blocks) result.push(await resolve(block));
326
+ return result;
327
+ }
328
+
329
+ export type SlackSentMessage = Readonly<{ channel: string; ts: string; url: string; thread_ts?: string; replies?: readonly SlackSentMessage[] }>;
330
+
331
+ export function sendSlackMessage(runtime: RuntimeCapabilities, destination: string, markdown: string): Promise<SlackSentMessage> {
332
+ return writeSlackMessage(runtime, destination, markdown, false);
333
+ }
334
+
335
+ export function editSlackMessage(runtime: RuntimeCapabilities, url: string, markdown: string): Promise<SlackSentMessage> {
336
+ return writeSlackMessage(runtime, url, markdown, true);
337
+ }
267
338
 
268
- export async function sendSlackMessage(runtime: RuntimeCapabilities, destination: string, markdown: string): Promise<SlackSentMessage> {
269
- const blocks = markdownToSlackBlocks(markdown);
339
+ async function writeSlackMessage(runtime: RuntimeCapabilities, destination: string, markdown: string, edit: boolean): Promise<SlackSentMessage> {
340
+ markdown = markdown.replace(/\r\n?/g, "\n");
341
+ const parts = slackMessageParts(markdown);
342
+ const blocks = parts.map(markdownToSlackBlocks);
343
+ const users = new Map<string, string>();
344
+ for (const [index, value] of blocks.entries()) blocks[index] = await resolveSlackEmailMentions(runtime, value, users);
345
+ if (edit && (!destination.startsWith("https://") || messageParts(new URL(destination)) === undefined)) throw new Error("Slack edit requires a message URL");
270
346
  const session = await slackSession(runtime);
271
347
  const request = (method: string, parameters: Readonly<Record<string, string>>) => api(runtime, session.origin, session.token, session.cookie, method, parameters);
272
348
  let channel: string;
273
349
  let thread: string | undefined;
350
+ let editTimestamp: string | undefined;
274
351
  if (destination.startsWith("https://")) {
275
352
  const url = new URL(destination);
276
353
  const message = messageParts(url);
@@ -278,25 +355,19 @@ export async function sendSlackMessage(runtime: RuntimeCapabilities, destination
278
355
  if (message === undefined && channelId === undefined) throw new Error(`Unsupported Slack destination: ${destination}`);
279
356
  if (message !== undefined) {
280
357
  channel = message.channel;
358
+ if (edit) editTimestamp = slackTimestamp(message.rawTimestamp);
281
359
  thread = url.searchParams.has("thread_ts") ? url.searchParams.get("thread_ts")! : slackTimestamp(message.rawTimestamp);
282
360
  const response = await request("conversations.replies", { channel, ts: thread, limit: "1" });
283
361
  const selected = (response["messages"] as readonly JsonObject[])[0]!;
284
362
  if (selected["thread_ts"] !== undefined) thread = selected["thread_ts"] as string;
285
363
  } else channel = channelId!;
286
364
  } else if (/^[CDG][A-Z0-9]+$/.test(destination)) channel = destination;
287
- else if (/^[UW][A-Z0-9]+$/.test(destination) || destination.startsWith("@")) {
365
+ else if (/^[UW][A-Z0-9]+$/.test(destination) || destination.startsWith("@") || /^[^@\s]+@[^@\s]+$/.test(destination)) {
288
366
  let user = destination;
289
- if (destination.startsWith("@")) {
290
- const name = destination.slice(1);
291
- const found: string[] = [];
292
- let cursor: string | undefined;
293
- do {
294
- const response = await request("users.list", { limit: "200", ...(cursor === undefined ? {} : { cursor }) });
295
- for (const member of response["members"] as readonly JsonObject[]) if (member["name"] === name && member["deleted"] !== true) found.push(member["id"] as string);
296
- cursor = (response["response_metadata"] as JsonObject | undefined)?.["next_cursor"] as string | undefined;
297
- } while (cursor !== undefined && cursor !== "");
298
- if (found.length !== 1) throw new Error(`Expected one Slack user named @${name}, found ${found.length}; use a user ID`);
299
- user = found[0]!;
367
+ if (!/^[UW][A-Z0-9]+$/.test(destination)) {
368
+ const found = await lookupSlackUsers(runtime, destination);
369
+ if (found.length !== 1) throw new Error(`Expected one Slack user for ${destination}, found ${found.length}; use an email or user ID`);
370
+ user = found[0]!["id"] as string;
300
371
  }
301
372
  const opened = await request("conversations.open", { users: user });
302
373
  channel = (opened["channel"] as JsonObject)["id"] as string;
@@ -314,10 +385,21 @@ export async function sendSlackMessage(runtime: RuntimeCapabilities, destination
314
385
  if (found.length !== 1) throw new Error(`Expected one Slack channel named #${name}, found ${found.length}; use a channel ID or URL`);
315
386
  channel = found[0]!;
316
387
  }
317
- const posted = await request("chat.postMessage", { channel, text: markdown, blocks: JSON.stringify(blocks), unfurl_links: "false", unfurl_media: "false", parse: "none", mrkdwn: "false", ...(thread === undefined ? {} : { thread_ts: thread }) });
318
- const ts = posted["ts"] as string;
319
- const url = `${session.origin}/archives/${posted["channel"]}/p${ts.replace(".", "")}${thread === undefined ? "" : `?thread_ts=${thread}`}`;
320
- return { channel: posted["channel"] as string, ts, url, ...(thread === undefined ? {} : { thread_ts: thread }) };
388
+ const sent: SlackSentMessage[] = [];
389
+ for (const [index, part] of parts.entries()) {
390
+ try {
391
+ const updating = index === 0 && edit;
392
+ const posted = await request(updating ? "chat.update" : "chat.postMessage", { channel, text: part, blocks: JSON.stringify(blocks[index]), unfurl_links: "false", unfurl_media: "false", parse: "none", mrkdwn: "false", ...(updating ? { ts: editTimestamp! } : thread === undefined ? {} : { thread_ts: thread }) });
393
+ const ts = posted["ts"] as string;
394
+ const url = `${session.origin}/archives/${posted["channel"]}/p${ts.replace(".", "")}${thread === undefined ? "" : `?thread_ts=${thread}`}`;
395
+ sent.push({ channel: posted["channel"] as string, ts, url, ...(thread === undefined ? {} : { thread_ts: thread }) });
396
+ thread ??= ts;
397
+ } catch (error) {
398
+ if (sent.length === 0) throw error;
399
+ throw new Error(`Slack thread stopped after ${sent.length} message(s): ${sent.map((message) => message.url).join(", ")}. ${error instanceof Error ? error.message : String(error)}`);
400
+ }
401
+ }
402
+ return { ...sent[0]!, ...(sent.length === 1 ? {} : { replies: sent.slice(1) }) };
321
403
  }
322
404
 
323
405
  export const slackService = defineService<RuntimeCapabilities>({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kamilio/wire",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
4
4
  "description": "Attach Notion, Google Docs, Slack, Gmail, Asana, ChatGPT, and Zoom resources as local Markdown you can sync.",
5
5
  "keywords": [
6
6
  "markdown",
@@ -1,6 +1,6 @@
1
1
  import { UserError, defineCommand, defineGroup, S, type Group } from "toolcraft";
2
2
 
3
- import { defaultWireBackend, registryPathForBackend, type FetchedDocument, type InitializedWire, type Resource, type SwitchedWireBackend, type Wire, type WireBackend, type WireResult, type WireWatchSession } from "wire-core";
3
+ import { defaultWireBackend, registryPathForBackend, type JsonObject, type FetchedDocument, type InitializedWire, type Resource, type SwitchedWireBackend, type Wire, type WireBackend, type WireResult, type WireWatchSession } from "wire-core";
4
4
  import type { SlackSentMessage } from "provider-slack";
5
5
  import type { Auth, AuthResult, AuthService } from "../auth.js";
6
6
 
@@ -415,7 +415,7 @@ function authGroup(service: AuthService, description: string, loginDescription:
415
415
  ] });
416
416
  }
417
417
 
418
- export function createRoot(wire: Wire, currentDirectory: string, auth?: Auth, readInput?: TextInputReader, options: WireRootOptions = { allowPaste: true }, sendSlack?: (destination: string, markdown: string) => Promise<SlackSentMessage>): WireRoot {
418
+ export function createRoot(wire: Wire, currentDirectory: string, auth?: Auth, readInput?: TextInputReader, options: WireRootOptions = { allowPaste: true }, sendSlack?: (destination: string, markdown: string) => Promise<SlackSentMessage>, lookupSlack?: (query: string) => Promise<readonly JsonObject[]>, editSlack?: (url: string, markdown: string) => Promise<SlackSentMessage>, lookupNotion?: (query: string) => Promise<readonly JsonObject[]>): WireRoot {
419
419
  const attachFromCliUrl = (url: string) => {
420
420
  const removedCommand = removedCliCommandNames[url as keyof typeof removedCliCommandNames];
421
421
  if (removedCommand !== undefined) throw new UserError(`Unknown command: ${url}. ${removedCommand}`);
@@ -449,14 +449,33 @@ export function createRoot(wire: Wire, currentDirectory: string, auth?: Auth, re
449
449
  const authCommand = (service: (typeof authServices)[number]) => {
450
450
  const title = serviceTitle(service);
451
451
  const group = authGroup(service, `Manage ${title} login.`, "Capture cookies once; normal commands reuse saved cookies.", auth!, readInput, options);
452
- if (service !== "slack") return group;
453
- return defineGroup({ name: "slack", description: "Read Slack channels, DMs, and threads, send Markdown, and manage login.", children: [preview, defaultAttach, sync, ...group.children, ...(sendSlack === undefined ? [] : [defineCommand({
452
+ const lookup = service === "slack" ? lookupSlack : service === "notion" ? lookupNotion : undefined;
453
+ const users = lookup === undefined ? [] : [defineGroup({ name: "users", description: "Find people by email or name.", children: [defineCommand({
454
+ name: "lookup",
455
+ description: "Look up active users by exact email or name. Email lookup avoids ambiguous names.",
456
+ positional: ["query"],
457
+ params: S.Object({ query: S.String({ description: "Email address or name to look up." }) }),
458
+ handler: ({ params }) => userFacing(() => lookup(params.query)),
459
+ render: render<readonly JsonObject[]>((results) => results.length === 0 ? "No matching users" : results.map((user) => {
460
+ const profile = user["profile"] as JsonObject | undefined;
461
+ return service === "slack" ? `${user["id"]} ${user["real_name"] ?? user["name"]} ${profile?.["email"] ?? ""}` : `${user["userId"]} ${user["name"] ?? ""} ${(user["aliases"] as readonly string[] | undefined ?? []).join(", ")}`;
462
+ }).join("\n")),
463
+ })] })];
464
+ if (service !== "slack") return users.length === 0 ? group : defineGroup({ ...group, children: [...group.children, ...users] });
465
+ return defineGroup({ name: "slack", description: "Read Slack channels, DMs, and threads, send Markdown, and manage login.", children: [preview, defaultAttach, sync, ...group.children, ...users, ...(sendSlack === undefined ? [] : [defineCommand({
454
466
  name: "send",
455
- description: "Send Markdown to a channel, @username, user ID, DM URL, or thread URL. Use a thread URL to reply. Tables and frontmatter are sent as code blocks.",
467
+ description: "Send Markdown to a channel, email, @username, user ID, or URL. Top-level --- separators split it into a parent and thread replies; a thread URL replies there.",
456
468
  positional: ["destination", "markdown"],
457
- params: S.Object({ destination: S.String({ description: "Slack channel name (with #), @username, user ID, conversation ID or URL, or thread URL to reply to." }), markdown: S.String({ description: "Message in Markdown. Supports formatting, nested lists, code, quotes, links, and explicit Slack mentions such as <@U123>." }) }),
469
+ params: S.Object({ destination: S.String({ description: "Slack channel name (with #), email, @username, user ID, conversation ID or URL, or thread URL to reply to." }), markdown: S.String({ description: "Message in Markdown. Mention by email with [@Name](mailto:email), or use <@U123>. Separate thread messages with blank lines around ---." }) }),
458
470
  handler: ({ params }) => userFacing(() => sendSlack!(params.destination, params.markdown)),
459
- render: render<SlackSentMessage>((result) => `Sent: ${result.url}`),
471
+ render: render<SlackSentMessage>((result) => `Sent: ${[result, ...result.replies ?? []].map((message) => message.url).join("\n")}`),
472
+ })]), ...(editSlack === undefined ? [] : [defineCommand({
473
+ name: "edit",
474
+ description: "Edit a message by URL. Sections after --- are appended as thread replies.",
475
+ positional: ["url", "markdown"],
476
+ params: S.Object({ url: S.String({ description: "URL of your Slack message to edit." }), markdown: S.String({ description: "Replacement Markdown, with email mentions and optional --- thread separators." }) }),
477
+ handler: ({ params }) => userFacing(() => editSlack(params.url, params.markdown)),
478
+ render: render<SlackSentMessage>((result) => `Updated: ${[result, ...result.replies ?? []].map((message) => message.url).join("\n")}`),
460
479
  })])] });
461
480
  };
462
481
  return defineGroup({
package/src/cli.ts CHANGED
@@ -240,7 +240,8 @@ function excessPositionals(argv: readonly string[]): Readonly<{ commandPath: str
240
240
  const command = words[0]!;
241
241
  if (command in fixedCommandPositionals) return Object.freeze({ commandPath: command, expected: fixedCommandPositionals[command as keyof typeof fixedCommandPositionals].expected, received: words.length - 1 });
242
242
  if (command === "slack" && slackResourceCommands.has(words[1]!)) return { commandPath: `slack ${words[1]}`, expected: 1, received: words.length - 2 };
243
- if (command === "slack" && words[1] === "send") return Object.freeze({ commandPath: "slack send", expected: 2, received: words.length - 2 });
243
+ if ((command === "slack" || command === "notion") && words[1] === "users" && words[2] === "lookup") return { commandPath: `${command} users lookup`, expected: 1, received: words.length - 3 };
244
+ if (command === "slack" && (words[1] === "send" || words[1] === "edit")) return { commandPath: `slack ${words[1]}`, expected: 2, received: words.length - 2 };
244
245
  if (authServices.has(command)) {
245
246
  const authCommand = words[1];
246
247
  if (authCommand === undefined || !authCommands.has(authCommand)) return null;
@@ -256,7 +257,8 @@ function missingPositionals(argv: readonly string[]): Readonly<{ commandPath: st
256
257
  if (words === null || words.length === 0) return null;
257
258
  const command = words[0]!;
258
259
  if (command === "slack" && slackResourceCommands.has(words[1]!) && words.length < 3) return { commandPath: `slack ${words[1]}`, name: words[1] === "sync" ? "resource" : "url" };
259
- if (command === "slack" && words[1] === "send" && words.length < 4) return { commandPath: "slack send", name: words.length === 2 ? "destination" : "markdown" };
260
+ if ((command === "slack" || command === "notion") && words[1] === "users" && words[2] === "lookup" && words.length < 4) return { commandPath: `${command} users lookup`, name: "query" };
261
+ if (command === "slack" && (words[1] === "send" || words[1] === "edit") && words.length < 4) return { commandPath: `slack ${words[1]}`, name: words.length === 2 ? words[1] === "send" ? "destination" : "url" : "markdown" };
260
262
  if (!(command in fixedCommandPositionals)) return null;
261
263
  const positional = fixedCommandPositionals[command as keyof typeof fixedCommandPositionals];
262
264
  if (words.length - 1 >= positional.expected || positional.expected === 0) return null;
@@ -292,7 +294,7 @@ function renderRemovedCommand(argv: readonly string[]): boolean {
292
294
  function renderUnknownAuthCommand(argv: readonly string[]): boolean {
293
295
  if (hasRootSeparator(argv)) return false;
294
296
  const words = positionalWords(argv);
295
- if (words === null || words.length < 2 || !authServices.has(words[0]!) || authCommands.has(words[1]!) || words[0] === "slack" && (words[1] === "send" || slackResourceCommands.has(words[1]!))) return false;
297
+ if (words === null || words.length < 2 || !authServices.has(words[0]!) || authCommands.has(words[1]!) || (words[0] === "slack" || words[0] === "notion") && words[1] === "users" || words[0] === "slack" && (words[1] === "send" || words[1] === "edit" || slackResourceCommands.has(words[1]!))) return false;
296
298
  renderCliError(argv, `Unknown command: ${words[1]}`, `wire ${words[0]} --help`, `Unknown command: ${cliValue(words[1]!)}`);
297
299
  return true;
298
300
  }
package/src/executable.ts CHANGED
@@ -2,7 +2,9 @@ import { existsSync, readFileSync, statSync } from "node:fs";
2
2
  import { mkdir, writeFile } from "node:fs/promises";
3
3
  import { dirname, isAbsolute, join, resolve } from "node:path";
4
4
 
5
- import { sendSlackMessage } from "provider-slack";
5
+ import { sendSlackMessage, editSlackMessage, lookupSlackUsers } from "provider-slack";
6
+
7
+ import { lookupNotionUsers } from "provider-notion";
6
8
 
7
9
  import { createRoot } from "./adapters/root.js";
8
10
  import { composeAuth } from "./auth.js";
@@ -127,5 +129,5 @@ export function createExecutableRoot(environment: NodeEnvironment, currentDirect
127
129
  switchBackend: baseWire.switchBackend,
128
130
  });
129
131
  const wire = withWireHooks(configuredWire, { currentDirectory, home, environment: configuredEnvironment() });
130
- return createRoot(wire, currentDirectory, auth, readStandardInput, options, (destination, markdown) => sendSlackMessage(runtime, destination, markdown));
132
+ return createRoot(wire, currentDirectory, auth, readStandardInput, options, (destination, markdown) => sendSlackMessage(runtime, destination, markdown), (query) => lookupSlackUsers(runtime, query), (url, markdown) => editSlackMessage(runtime, url, markdown), (query) => lookupNotionUsers(runtime, query));
131
133
  }