jinn-cli 0.7.4 → 0.7.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/connectors/slack/__tests__/format.test.d.ts +2 -0
- package/dist/src/connectors/slack/__tests__/format.test.d.ts.map +1 -0
- package/dist/src/connectors/slack/__tests__/format.test.js +115 -0
- package/dist/src/connectors/slack/__tests__/format.test.js.map +1 -0
- package/dist/src/connectors/slack/format.d.ts +7 -0
- package/dist/src/connectors/slack/format.d.ts.map +1 -1
- package/dist/src/connectors/slack/format.js +33 -3
- package/dist/src/connectors/slack/format.js.map +1 -1
- package/dist/src/connectors/whatsapp/__tests__/format.test.d.ts +2 -0
- package/dist/src/connectors/whatsapp/__tests__/format.test.d.ts.map +1 -0
- package/dist/src/connectors/whatsapp/__tests__/format.test.js +70 -0
- package/dist/src/connectors/whatsapp/__tests__/format.test.js.map +1 -0
- package/dist/src/connectors/whatsapp/format.d.ts +10 -0
- package/dist/src/connectors/whatsapp/format.d.ts.map +1 -1
- package/dist/src/connectors/whatsapp/format.js +34 -3
- package/dist/src/connectors/whatsapp/format.js.map +1 -1
- package/dist/src/gateway/api.d.ts.map +1 -1
- package/dist/src/gateway/api.js +35 -6
- package/dist/src/gateway/api.js.map +1 -1
- package/dist/src/gateway/files.d.ts.map +1 -1
- package/dist/src/gateway/files.js +9 -1
- package/dist/src/gateway/files.js.map +1 -1
- package/dist/web/404.html +1 -1
- package/dist/web/_next/static/chunks/155-5843f8840f40f6f8.js +1 -0
- package/dist/web/_next/static/chunks/192-f566317c5713c743.js +1 -0
- package/dist/web/_next/static/chunks/660-7b281827cf68988a.js +1 -0
- package/dist/web/_next/static/chunks/943.c30215b2dbec402b.js +1 -0
- package/dist/web/_next/static/chunks/app/chat/page-b111adcdff7746b0.js +1 -0
- package/dist/web/_next/static/chunks/app/org/page-b14614ed22c7a80d.js +1 -0
- package/dist/web/_next/static/chunks/app/settings/page-c2b014fb0706aa88.js +1 -0
- package/dist/web/_next/static/chunks/c967e59e-8414f3fe827ed369.js +1 -0
- package/dist/web/_next/static/chunks/{webpack-e7350efbcf65db9c.js → webpack-2e375360ad2078fe.js} +1 -1
- package/dist/web/_next/static/css/a2080d6b6696080a.css +1 -0
- package/dist/web/chat.html +1 -1
- package/dist/web/chat.txt +4 -4
- package/dist/web/cron.html +1 -1
- package/dist/web/cron.txt +4 -4
- package/dist/web/index.html +1 -1
- package/dist/web/index.txt +4 -4
- package/dist/web/kanban.html +1 -1
- package/dist/web/kanban.txt +4 -4
- package/dist/web/logs.html +2 -2
- package/dist/web/logs.txt +4 -4
- package/dist/web/org.html +1 -1
- package/dist/web/org.txt +4 -4
- package/dist/web/sessions.html +1 -1
- package/dist/web/sessions.txt +3 -3
- package/dist/web/settings.html +1 -1
- package/dist/web/settings.txt +4 -4
- package/dist/web/skills.html +1 -1
- package/dist/web/skills.txt +4 -4
- package/package.json +1 -1
- package/dist/web/_next/static/chunks/155-277bf3022d16e74f.js +0 -1
- package/dist/web/_next/static/chunks/192-28e0cb7a172d804d.js +0 -1
- package/dist/web/_next/static/chunks/865-b4eb9a132b937321.js +0 -1
- package/dist/web/_next/static/chunks/943.1c6d37432bcad8e8.js +0 -1
- package/dist/web/_next/static/chunks/app/chat/page-d9b12d7effe66c59.js +0 -1
- package/dist/web/_next/static/chunks/app/org/page-e51ef93389a23de8.js +0 -1
- package/dist/web/_next/static/chunks/app/settings/page-8d1048b944663524.js +0 -1
- package/dist/web/_next/static/css/92635e77ae2cd8fd.css +0 -1
- /package/dist/web/_next/static/{er4Sne2nYT8t4rNVzckgy → ec5FmQvpr0g4TGeSgU_z_}/_buildManifest.js +0 -0
- /package/dist/web/_next/static/{er4Sne2nYT8t4rNVzckgy → ec5FmQvpr0g4TGeSgU_z_}/_ssgManifest.js +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.test.d.ts","sourceRoot":"","sources":["../../../../../src/connectors/slack/__tests__/format.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { markdownToSlackMrkdwn, formatResponse } from "../format.js";
|
|
3
|
+
describe("markdownToSlackMrkdwn", () => {
|
|
4
|
+
describe("headings", () => {
|
|
5
|
+
it("converts ## headings to bold on own line", () => {
|
|
6
|
+
expect(markdownToSlackMrkdwn("## My Heading")).toBe("*My Heading*");
|
|
7
|
+
});
|
|
8
|
+
it("converts ### headings to bold", () => {
|
|
9
|
+
expect(markdownToSlackMrkdwn("### Sub Heading")).toBe("*Sub Heading*");
|
|
10
|
+
});
|
|
11
|
+
it("converts # h1 to bold", () => {
|
|
12
|
+
expect(markdownToSlackMrkdwn("# Title")).toBe("*Title*");
|
|
13
|
+
});
|
|
14
|
+
it("converts headings with up to 6 levels", () => {
|
|
15
|
+
expect(markdownToSlackMrkdwn("###### Deep")).toBe("*Deep*");
|
|
16
|
+
});
|
|
17
|
+
it("only converts headings at start of line", () => {
|
|
18
|
+
expect(markdownToSlackMrkdwn("not a ## heading")).toBe("not a ## heading");
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
describe("bold", () => {
|
|
22
|
+
it("converts **bold** to *bold*", () => {
|
|
23
|
+
expect(markdownToSlackMrkdwn("this is **bold** text")).toBe("this is *bold* text");
|
|
24
|
+
});
|
|
25
|
+
it("converts __bold__ to *bold*", () => {
|
|
26
|
+
expect(markdownToSlackMrkdwn("this is __bold__ text")).toBe("this is *bold* text");
|
|
27
|
+
});
|
|
28
|
+
it("handles multiple bold segments", () => {
|
|
29
|
+
expect(markdownToSlackMrkdwn("**a** and **b**")).toBe("*a* and *b*");
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
describe("italic", () => {
|
|
33
|
+
it("converts _italic_ to _italic_ (passthrough)", () => {
|
|
34
|
+
expect(markdownToSlackMrkdwn("this is _italic_ text")).toBe("this is _italic_ text");
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
describe("strikethrough", () => {
|
|
38
|
+
it("converts ~~strike~~ to ~strike~", () => {
|
|
39
|
+
expect(markdownToSlackMrkdwn("this is ~~struck~~ out")).toBe("this is ~struck~ out");
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
describe("links", () => {
|
|
43
|
+
it("converts [text](url) to <url|text>", () => {
|
|
44
|
+
expect(markdownToSlackMrkdwn("click [here](https://example.com) now")).toBe("click <https://example.com|here> now");
|
|
45
|
+
});
|
|
46
|
+
it("converts multiple links", () => {
|
|
47
|
+
expect(markdownToSlackMrkdwn("[a](http://a.com) and [b](http://b.com)")).toBe("<http://a.com|a> and <http://b.com|b>");
|
|
48
|
+
});
|
|
49
|
+
it("handles bare URLs (no conversion needed)", () => {
|
|
50
|
+
expect(markdownToSlackMrkdwn("visit https://example.com")).toBe("visit https://example.com");
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
describe("bullet lists", () => {
|
|
54
|
+
it("converts - item to • item", () => {
|
|
55
|
+
expect(markdownToSlackMrkdwn("- first\n- second")).toBe("• first\n• second");
|
|
56
|
+
});
|
|
57
|
+
it("converts * item to • item", () => {
|
|
58
|
+
expect(markdownToSlackMrkdwn("* first\n* second")).toBe("• first\n• second");
|
|
59
|
+
});
|
|
60
|
+
it("preserves indented sub-items", () => {
|
|
61
|
+
expect(markdownToSlackMrkdwn("- top\n - nested")).toBe("• top\n • nested");
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
describe("code (preserved)", () => {
|
|
65
|
+
it("preserves inline code", () => {
|
|
66
|
+
expect(markdownToSlackMrkdwn("use `console.log`")).toBe("use `console.log`");
|
|
67
|
+
});
|
|
68
|
+
it("preserves code blocks", () => {
|
|
69
|
+
const input = "```\nconst x = 1;\n```";
|
|
70
|
+
expect(markdownToSlackMrkdwn(input)).toBe("```\nconst x = 1;\n```");
|
|
71
|
+
});
|
|
72
|
+
it("does not convert markdown inside code blocks", () => {
|
|
73
|
+
const input = "```\n## not a heading\n**not bold**\n```";
|
|
74
|
+
expect(markdownToSlackMrkdwn(input)).toBe("```\n## not a heading\n**not bold**\n```");
|
|
75
|
+
});
|
|
76
|
+
it("does not convert markdown inside inline code", () => {
|
|
77
|
+
expect(markdownToSlackMrkdwn("use `**not bold**`")).toBe("use `**not bold**`");
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
describe("blockquotes", () => {
|
|
81
|
+
it("preserves > blockquotes (Slack supports them)", () => {
|
|
82
|
+
expect(markdownToSlackMrkdwn("> quoted text")).toBe("> quoted text");
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
describe("numbered lists", () => {
|
|
86
|
+
it("preserves numbered lists as-is", () => {
|
|
87
|
+
expect(markdownToSlackMrkdwn("1. first\n2. second")).toBe("1. first\n2. second");
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
describe("complex mixed content", () => {
|
|
91
|
+
it("handles headings + bold + links together", () => {
|
|
92
|
+
const input = "## Summary\n\nThis is **important** and [see docs](https://docs.com).";
|
|
93
|
+
const expected = "*Summary*\n\nThis is *important* and <https://docs.com|see docs>.";
|
|
94
|
+
expect(markdownToSlackMrkdwn(input)).toBe(expected);
|
|
95
|
+
});
|
|
96
|
+
it("handles text between code blocks", () => {
|
|
97
|
+
const input = "Before\n```\ncode\n```\n**after**";
|
|
98
|
+
const expected = "Before\n```\ncode\n```\n*after*";
|
|
99
|
+
expect(markdownToSlackMrkdwn(input)).toBe(expected);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
describe("formatResponse", () => {
|
|
104
|
+
it("applies markdown conversion before chunking", () => {
|
|
105
|
+
const result = formatResponse("## Hello\n\nThis is **bold**.");
|
|
106
|
+
expect(result).toEqual(["*Hello*\n\nThis is *bold*."]);
|
|
107
|
+
});
|
|
108
|
+
it("still chunks long messages after conversion", () => {
|
|
109
|
+
const longText = "## Title\n\n" + "word ".repeat(1000);
|
|
110
|
+
const result = formatResponse(longText);
|
|
111
|
+
expect(result.length).toBeGreaterThan(1);
|
|
112
|
+
expect(result[0].startsWith("*Title*")).toBe(true);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
//# sourceMappingURL=format.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.test.js","sourceRoot":"","sources":["../../../../../src/connectors/slack/__tests__/format.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert standard markdown to Slack mrkdwn format.
|
|
3
|
+
* Handles headings, bold, strikethrough, links, and bullet lists.
|
|
4
|
+
* Preserves code blocks and inline code untouched.
|
|
5
|
+
*/
|
|
6
|
+
export declare function markdownToSlackMrkdwn(text: string): string;
|
|
1
7
|
/**
|
|
2
8
|
* Split text into chunks that fit within Slack's message length limit.
|
|
9
|
+
* Converts markdown to Slack mrkdwn format before chunking.
|
|
3
10
|
*/
|
|
4
11
|
export declare function formatResponse(text: string): string[];
|
|
5
12
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../../../src/connectors/slack/format.ts"],"names":[],"mappings":"AAMA
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../../../src/connectors/slack/format.ts"],"names":[],"mappings":"AAMA;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAyB1D;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAgCrD;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,CAAC,CAqBjB"}
|
|
@@ -2,15 +2,45 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
4
|
const SLACK_MAX_LENGTH = 3000;
|
|
5
|
+
/**
|
|
6
|
+
* Convert standard markdown to Slack mrkdwn format.
|
|
7
|
+
* Handles headings, bold, strikethrough, links, and bullet lists.
|
|
8
|
+
* Preserves code blocks and inline code untouched.
|
|
9
|
+
*/
|
|
10
|
+
export function markdownToSlackMrkdwn(text) {
|
|
11
|
+
// Split text into code and non-code segments to protect code from conversion
|
|
12
|
+
const segments = text.split(/(```[\s\S]*?```|`[^`]+`)/g);
|
|
13
|
+
return segments
|
|
14
|
+
.map((segment, i) => {
|
|
15
|
+
// Odd indices are code matches — leave them untouched
|
|
16
|
+
if (i % 2 === 1)
|
|
17
|
+
return segment;
|
|
18
|
+
return (segment
|
|
19
|
+
// Headings: ## text → *text* (must be at start of line)
|
|
20
|
+
.replace(/^(#{1,6})\s+(.+)$/gm, (_match, _hashes, content) => `*${content}*`)
|
|
21
|
+
// Bold: **text** or __text__ → *text*
|
|
22
|
+
.replace(/\*\*(.+?)\*\*/g, "*$1*")
|
|
23
|
+
.replace(/__(.+?)__/g, "*$1*")
|
|
24
|
+
// Strikethrough: ~~text~~ → ~text~
|
|
25
|
+
.replace(/~~(.+?)~~/g, "~$1~")
|
|
26
|
+
// Links: [text](url) → <url|text>
|
|
27
|
+
.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "<$2|$1>")
|
|
28
|
+
// Bullet lists: - item or * item → • item (with optional indentation)
|
|
29
|
+
.replace(/^(\s*)[-*]\s+/gm, "$1• "));
|
|
30
|
+
})
|
|
31
|
+
.join("");
|
|
32
|
+
}
|
|
5
33
|
/**
|
|
6
34
|
* Split text into chunks that fit within Slack's message length limit.
|
|
35
|
+
* Converts markdown to Slack mrkdwn format before chunking.
|
|
7
36
|
*/
|
|
8
37
|
export function formatResponse(text) {
|
|
9
|
-
|
|
10
|
-
|
|
38
|
+
const converted = markdownToSlackMrkdwn(text);
|
|
39
|
+
if (converted.length <= SLACK_MAX_LENGTH) {
|
|
40
|
+
return [converted];
|
|
11
41
|
}
|
|
12
42
|
const chunks = [];
|
|
13
|
-
let remaining =
|
|
43
|
+
let remaining = converted;
|
|
14
44
|
while (remaining.length > 0) {
|
|
15
45
|
if (remaining.length <= SLACK_MAX_LENGTH) {
|
|
16
46
|
chunks.push(remaining);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../../../src/connectors/slack/format.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAE9B
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../../../src/connectors/slack/format.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.test.d.ts","sourceRoot":"","sources":["../../../../../src/connectors/whatsapp/__tests__/format.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { markdownToWhatsApp, formatResponse } from "../format.js";
|
|
3
|
+
describe("markdownToWhatsApp", () => {
|
|
4
|
+
describe("headings", () => {
|
|
5
|
+
it("converts ## headings to *bold* on own line", () => {
|
|
6
|
+
expect(markdownToWhatsApp("## My Heading")).toBe("*My Heading*");
|
|
7
|
+
});
|
|
8
|
+
it("converts # h1 to bold", () => {
|
|
9
|
+
expect(markdownToWhatsApp("# Title")).toBe("*Title*");
|
|
10
|
+
});
|
|
11
|
+
});
|
|
12
|
+
describe("bold", () => {
|
|
13
|
+
it("converts **bold** to *bold*", () => {
|
|
14
|
+
expect(markdownToWhatsApp("this is **bold** text")).toBe("this is *bold* text");
|
|
15
|
+
});
|
|
16
|
+
it("converts __bold__ to *bold*", () => {
|
|
17
|
+
expect(markdownToWhatsApp("this is __bold__ text")).toBe("this is *bold* text");
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
describe("italic", () => {
|
|
21
|
+
it("preserves _italic_ (WhatsApp uses same syntax)", () => {
|
|
22
|
+
expect(markdownToWhatsApp("this is _italic_ text")).toBe("this is _italic_ text");
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
describe("strikethrough", () => {
|
|
26
|
+
it("converts ~~strike~~ to ~strike~", () => {
|
|
27
|
+
expect(markdownToWhatsApp("this is ~~struck~~ out")).toBe("this is ~struck~ out");
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
describe("links", () => {
|
|
31
|
+
it("converts [text](url) to text (url) since WA auto-links", () => {
|
|
32
|
+
expect(markdownToWhatsApp("click [here](https://example.com)")).toBe("click here (https://example.com)");
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
describe("bullet lists", () => {
|
|
36
|
+
it("converts - item to • item", () => {
|
|
37
|
+
expect(markdownToWhatsApp("- first\n- second")).toBe("• first\n• second");
|
|
38
|
+
});
|
|
39
|
+
it("converts * item to • item (not bold)", () => {
|
|
40
|
+
expect(markdownToWhatsApp("* first\n* second")).toBe("• first\n• second");
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
describe("code", () => {
|
|
44
|
+
it("preserves inline code", () => {
|
|
45
|
+
expect(markdownToWhatsApp("use `code`")).toBe("use `code`");
|
|
46
|
+
});
|
|
47
|
+
it("preserves code blocks", () => {
|
|
48
|
+
const input = "```\ncode here\n```";
|
|
49
|
+
expect(markdownToWhatsApp(input)).toBe("```\ncode here\n```");
|
|
50
|
+
});
|
|
51
|
+
it("does not convert markdown inside code blocks", () => {
|
|
52
|
+
const input = "```\n## not a heading\n```";
|
|
53
|
+
expect(markdownToWhatsApp(input)).toBe("```\n## not a heading\n```");
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
describe("mixed content", () => {
|
|
57
|
+
it("handles headings + bold + links", () => {
|
|
58
|
+
const input = "## Summary\n\nThis is **important** and [docs](https://docs.com).";
|
|
59
|
+
const expected = "*Summary*\n\nThis is *important* and docs (https://docs.com).";
|
|
60
|
+
expect(markdownToWhatsApp(input)).toBe(expected);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
describe("formatResponse", () => {
|
|
65
|
+
it("applies markdown conversion before chunking", () => {
|
|
66
|
+
const result = formatResponse("## Hello\n\n**bold** text");
|
|
67
|
+
expect(result).toEqual(["*Hello*\n\n*bold* text"]);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
//# sourceMappingURL=format.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.test.js","sourceRoot":"","sources":["../../../../../src/connectors/whatsapp/__tests__/format.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,2 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert standard markdown to WhatsApp formatting.
|
|
3
|
+
* WhatsApp supports *bold*, _italic_, ~strikethrough~, ```code```, `code`.
|
|
4
|
+
* It does NOT support headings or hyperlinks, so we convert those.
|
|
5
|
+
*/
|
|
6
|
+
export declare function markdownToWhatsApp(text: string): string;
|
|
7
|
+
/**
|
|
8
|
+
* Split text into chunks that fit within WhatsApp's message length limit.
|
|
9
|
+
* Converts markdown to WhatsApp formatting before chunking.
|
|
10
|
+
*/
|
|
1
11
|
export declare function formatResponse(text: string): string[];
|
|
2
12
|
//# sourceMappingURL=format.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../../../src/connectors/whatsapp/format.ts"],"names":[],"mappings":"AAEA,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../../../src/connectors/whatsapp/format.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAuBvD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAerD"}
|
|
@@ -1,9 +1,40 @@
|
|
|
1
1
|
const WA_MAX_LENGTH = 4000;
|
|
2
|
+
/**
|
|
3
|
+
* Convert standard markdown to WhatsApp formatting.
|
|
4
|
+
* WhatsApp supports *bold*, _italic_, ~strikethrough~, ```code```, `code`.
|
|
5
|
+
* It does NOT support headings or hyperlinks, so we convert those.
|
|
6
|
+
*/
|
|
7
|
+
export function markdownToWhatsApp(text) {
|
|
8
|
+
const segments = text.split(/(```[\s\S]*?```|`[^`]+`)/g);
|
|
9
|
+
return segments
|
|
10
|
+
.map((segment, i) => {
|
|
11
|
+
if (i % 2 === 1)
|
|
12
|
+
return segment;
|
|
13
|
+
return (segment
|
|
14
|
+
// Headings: ## text → *text* (bold on own line)
|
|
15
|
+
.replace(/^(#{1,6})\s+(.+)$/gm, (_match, _hashes, content) => `*${content}*`)
|
|
16
|
+
// Bold: **text** or __text__ → *text*
|
|
17
|
+
.replace(/\*\*(.+?)\*\*/g, "*$1*")
|
|
18
|
+
.replace(/__(.+?)__/g, "*$1*")
|
|
19
|
+
// Strikethrough: ~~text~~ → ~text~
|
|
20
|
+
.replace(/~~(.+?)~~/g, "~$1~")
|
|
21
|
+
// Links: [text](url) → text (url) — WhatsApp auto-links URLs
|
|
22
|
+
.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "$1 ($2)")
|
|
23
|
+
// Bullet lists: - item or * item → • item
|
|
24
|
+
.replace(/^(\s*)[-*]\s+/gm, "$1• "));
|
|
25
|
+
})
|
|
26
|
+
.join("");
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Split text into chunks that fit within WhatsApp's message length limit.
|
|
30
|
+
* Converts markdown to WhatsApp formatting before chunking.
|
|
31
|
+
*/
|
|
2
32
|
export function formatResponse(text) {
|
|
3
|
-
|
|
4
|
-
|
|
33
|
+
const converted = markdownToWhatsApp(text);
|
|
34
|
+
if (converted.length <= WA_MAX_LENGTH)
|
|
35
|
+
return [converted];
|
|
5
36
|
const chunks = [];
|
|
6
|
-
let remaining =
|
|
37
|
+
let remaining = converted;
|
|
7
38
|
while (remaining.length > 0) {
|
|
8
39
|
if (remaining.length <= WA_MAX_LENGTH) {
|
|
9
40
|
chunks.push(remaining);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../../../src/connectors/whatsapp/format.ts"],"names":[],"mappings":"AAAA,MAAM,aAAa,GAAG,IAAI,CAAC;AAE3B,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,IAAI,IAAI,CAAC,MAAM,IAAI,aAAa;QAAE,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../../../src/connectors/whatsapp/format.ts"],"names":[],"mappings":"AAAA,MAAM,aAAa,GAAG,IAAI,CAAC;AAE3B;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC;IAEzD,OAAO,QAAQ;SACZ,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE;QAClB,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;YAAE,OAAO,OAAO,CAAC;QAEhC,OAAO,CACL,OAAO;YACL,gDAAgD;aAC/C,OAAO,CAAC,qBAAqB,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,CAAC,IAAI,OAAO,GAAG,CAAC;YAC7E,sCAAsC;aACrC,OAAO,CAAC,gBAAgB,EAAE,MAAM,CAAC;aACjC,OAAO,CAAC,YAAY,EAAE,MAAM,CAAC;YAC9B,mCAAmC;aAClC,OAAO,CAAC,YAAY,EAAE,MAAM,CAAC;YAC9B,6DAA6D;aAC5D,OAAO,CAAC,0BAA0B,EAAE,SAAS,CAAC;YAC/C,0CAA0C;aACzC,OAAO,CAAC,iBAAiB,EAAE,MAAM,CAAC,CACtC,CAAC;IACJ,CAAC,CAAC;SACD,IAAI,CAAC,EAAE,CAAC,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,MAAM,SAAS,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAE3C,IAAI,SAAS,CAAC,MAAM,IAAI,aAAa;QAAE,OAAO,CAAC,SAAS,CAAC,CAAC;IAC1D,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,SAAS,GAAG,SAAS,CAAC;IAC1B,OAAO,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,IAAI,SAAS,CAAC,MAAM,IAAI,aAAa,EAAE,CAAC;YAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAAC,MAAM;QAAC,CAAC;QACzE,IAAI,KAAK,GAAG,SAAS,CAAC,WAAW,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;QACvD,IAAI,KAAK,IAAI,CAAC;YAAE,KAAK,GAAG,SAAS,CAAC,WAAW,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;QAClE,IAAI,KAAK,IAAI,CAAC;YAAE,KAAK,GAAG,aAAa,CAAC;QACtC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;QACvC,SAAS,GAAG,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,SAAS,EAAE,CAAC;IACjD,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../src/gateway/api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,IAAI,WAAW,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAMhF,OAAO,KAAK,EAAoC,UAAU,EAAmB,MAAM,oBAAoB,CAAC;AAExG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../src/gateway/api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,IAAI,WAAW,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAMhF,OAAO,KAAK,EAAoC,UAAU,EAAmB,MAAM,oBAAoB,CAAC;AAExG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AA4C7D,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,UAAU,CAAC;IACnB,cAAc,EAAE,cAAc,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,UAAU,CAAC;IAC5B,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAChD,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,oBAAoB,EAAE,SAAS,CAAC,CAAC;CACjE;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,UAAU,GAAG,IAAI,CAgCpE;AAgND,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,WAAW,EAChB,GAAG,EAAE,cAAc,EACnB,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,IAAI,CAAC,CAwxCf"}
|
package/dist/src/gateway/api.js
CHANGED
|
@@ -5,8 +5,8 @@ import path from "node:path";
|
|
|
5
5
|
import yaml from "js-yaml";
|
|
6
6
|
import { isInterruptibleEngine } from "../shared/types.js";
|
|
7
7
|
import { buildContext } from "../sessions/context.js";
|
|
8
|
-
import { initDb, listSessions, getSession, createSession, updateSession, deleteSession, deleteSessions, insertMessage, getMessages, enqueueQueueItem, cancelQueueItem, getQueueItems, cancelAllPendingQueueItems, listAllPendingQueueItems, } from "../sessions/registry.js";
|
|
9
|
-
import { CONFIG_PATH, CRON_RUNS, ORG_DIR, SKILLS_DIR, LOGS_DIR, TMP_DIR, } from "../shared/paths.js";
|
|
8
|
+
import { initDb, listSessions, getSession, createSession, updateSession, deleteSession, deleteSessions, insertMessage, getMessages, enqueueQueueItem, cancelQueueItem, getQueueItems, cancelAllPendingQueueItems, listAllPendingQueueItems, getFile, } from "../sessions/registry.js";
|
|
9
|
+
import { CONFIG_PATH, CRON_RUNS, ORG_DIR, SKILLS_DIR, LOGS_DIR, TMP_DIR, FILES_DIR, } from "../shared/paths.js";
|
|
10
10
|
import { logger } from "../shared/logger.js";
|
|
11
11
|
import { getSttStatus, downloadModel, transcribe as sttTranscribe, resolveLanguages, WHISPER_LANGUAGES } from "../stt/stt.js";
|
|
12
12
|
import { JINN_HOME } from "../shared/paths.js";
|
|
@@ -94,7 +94,7 @@ function dispatchWebSessionRun(session, prompt, engine, config, context, opts) {
|
|
|
94
94
|
const run = async () => {
|
|
95
95
|
await context.sessionManager.getQueue().enqueue(session.sessionKey || session.sourceRef, async () => {
|
|
96
96
|
context.emit("session:started", { sessionId: session.id });
|
|
97
|
-
await runWebSession(session, prompt, engine, config, context);
|
|
97
|
+
await runWebSession(session, prompt, engine, config, context, opts?.attachments);
|
|
98
98
|
}, opts?.queueItemId);
|
|
99
99
|
};
|
|
100
100
|
const launch = () => {
|
|
@@ -146,6 +146,32 @@ async function readJsonBody(req, res) {
|
|
|
146
146
|
return { ok: false };
|
|
147
147
|
}
|
|
148
148
|
}
|
|
149
|
+
/** Resolve an array of file IDs to local filesystem paths for engine consumption. */
|
|
150
|
+
function resolveAttachmentPaths(fileIds) {
|
|
151
|
+
if (!Array.isArray(fileIds))
|
|
152
|
+
return [];
|
|
153
|
+
const paths = [];
|
|
154
|
+
for (const id of fileIds) {
|
|
155
|
+
if (typeof id !== "string" || !id.trim())
|
|
156
|
+
continue;
|
|
157
|
+
const meta = getFile(id);
|
|
158
|
+
if (!meta) {
|
|
159
|
+
logger.warn(`Attachment file not found: ${id}`);
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
const filePath = path.join(FILES_DIR, meta.id, meta.filename);
|
|
163
|
+
if (fs.existsSync(filePath)) {
|
|
164
|
+
paths.push(filePath);
|
|
165
|
+
}
|
|
166
|
+
else if (meta.path && fs.existsSync(meta.path)) {
|
|
167
|
+
paths.push(meta.path);
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
logger.warn(`Attachment file missing on disk: ${id} (${meta.filename})`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return paths;
|
|
174
|
+
}
|
|
149
175
|
function json(res, data, status = 200) {
|
|
150
176
|
res.writeHead(status, { "Content-Type": "application/json" });
|
|
151
177
|
res.end(JSON.stringify(data));
|
|
@@ -514,10 +540,11 @@ export async function handleApiRequest(req, res, context) {
|
|
|
514
540
|
lastActivity: new Date().toISOString(),
|
|
515
541
|
});
|
|
516
542
|
session.status = "running";
|
|
543
|
+
const attachmentPaths = resolveAttachmentPaths(body.attachments);
|
|
517
544
|
const queueSessionKey = session.sessionKey || session.sourceRef || session.id;
|
|
518
545
|
const queueItemId = enqueueQueueItem(session.id, queueSessionKey, prompt);
|
|
519
546
|
context.emit("queue:updated", { sessionId: session.id, sessionKey: queueSessionKey });
|
|
520
|
-
dispatchWebSessionRun(session, prompt, engine, config, context, { queueItemId });
|
|
547
|
+
dispatchWebSessionRun(session, prompt, engine, config, context, { queueItemId, attachments: attachmentPaths.length > 0 ? attachmentPaths : undefined });
|
|
521
548
|
return json(res, serializeSession(session, context), 201);
|
|
522
549
|
}
|
|
523
550
|
// POST /api/sessions/:id/message
|
|
@@ -585,10 +612,11 @@ export async function handleApiRequest(req, res, context) {
|
|
|
585
612
|
}
|
|
586
613
|
// Clear any pending cancellation so the new message runs normally.
|
|
587
614
|
context.sessionManager.getQueue().clearCancelled(session.sessionKey || session.sourceRef || session.id);
|
|
615
|
+
const attachmentPaths = resolveAttachmentPaths(body.attachments);
|
|
588
616
|
const sessionKey = session.sessionKey || session.sourceRef || session.id;
|
|
589
617
|
const queueItemId = enqueueQueueItem(session.id, sessionKey, prompt);
|
|
590
618
|
context.emit("queue:updated", { sessionId: session.id, sessionKey });
|
|
591
|
-
dispatchWebSessionRun(session, prompt, engine, config, context, { queueItemId });
|
|
619
|
+
dispatchWebSessionRun(session, prompt, engine, config, context, { queueItemId, attachments: attachmentPaths.length > 0 ? attachmentPaths : undefined });
|
|
592
620
|
return json(res, { status: "queued", sessionId: session.id });
|
|
593
621
|
}
|
|
594
622
|
// GET /api/cron
|
|
@@ -1622,7 +1650,7 @@ function loadTranscriptMessages(engineSessionId) {
|
|
|
1622
1650
|
}
|
|
1623
1651
|
return [];
|
|
1624
1652
|
}
|
|
1625
|
-
async function runWebSession(session, prompt, engine, config, context) {
|
|
1653
|
+
async function runWebSession(session, prompt, engine, config, context, attachments) {
|
|
1626
1654
|
const currentSession = getSession(session.id);
|
|
1627
1655
|
if (!currentSession) {
|
|
1628
1656
|
logger.info(`Skipping deleted web session ${session.id} before run start`);
|
|
@@ -1687,6 +1715,7 @@ async function runWebSession(session, prompt, engine, config, context) {
|
|
|
1687
1715
|
model: currentSession.model ?? engineConfig.model,
|
|
1688
1716
|
effortLevel,
|
|
1689
1717
|
cliFlags: employee?.cliFlags,
|
|
1718
|
+
attachments: attachments?.length ? attachments : undefined,
|
|
1690
1719
|
sessionId: currentSession.id,
|
|
1691
1720
|
onStream: (delta) => {
|
|
1692
1721
|
const now = Date.now();
|