@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.
- package/README.md +13 -0
- package/dist/adapters/root.d.ts +2 -2
- package/dist/adapters/root.d.ts.map +1 -1
- package/dist/adapters/root.js +25 -6
- package/dist/adapters/root.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +9 -5
- package/dist/cli.js.map +1 -1
- package/dist/executable.d.ts.map +1 -1
- package/dist/executable.js +3 -2
- package/dist/executable.js.map +1 -1
- package/node_modules/provider-notion/dist/index.d.ts +1 -0
- package/node_modules/provider-notion/dist/index.d.ts.map +1 -1
- package/node_modules/provider-notion/dist/index.js +1 -0
- package/node_modules/provider-notion/dist/index.js.map +1 -1
- package/node_modules/provider-notion/dist/notion-sync.d.ts +3 -1
- package/node_modules/provider-notion/dist/notion-sync.d.ts.map +1 -1
- package/node_modules/provider-notion/dist/notion-sync.js +99 -26
- package/node_modules/provider-notion/dist/notion-sync.js.map +1 -1
- package/node_modules/provider-notion/src/index.ts +2 -0
- package/node_modules/provider-notion/src/notion-sync.ts +86 -25
- package/node_modules/provider-slack/dist/index.d.ts +1 -1
- package/node_modules/provider-slack/dist/index.d.ts.map +1 -1
- package/node_modules/provider-slack/dist/index.js +1 -1
- package/node_modules/provider-slack/dist/index.js.map +1 -1
- package/node_modules/provider-slack/dist/slack.d.ts +3 -0
- package/node_modules/provider-slack/dist/slack.d.ts.map +1 -1
- package/node_modules/provider-slack/dist/slack.js +111 -20
- package/node_modules/provider-slack/dist/slack.js.map +1 -1
- package/node_modules/provider-slack/src/index.ts +1 -1
- package/node_modules/provider-slack/src/slack.ts +101 -19
- package/package.json +1 -1
- package/src/adapters/root.ts +26 -7
- package/src/cli.ts +5 -3
- 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
|
|
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
|
-
|
|
269
|
-
|
|
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
|
|
290
|
-
const
|
|
291
|
-
|
|
292
|
-
|
|
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
|
|
318
|
-
const
|
|
319
|
-
|
|
320
|
-
|
|
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
package/src/adapters/root.ts
CHANGED
|
@@ -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
|
-
|
|
453
|
-
|
|
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,
|
|
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.
|
|
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] === "
|
|
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] === "
|
|
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
|
}
|