jinn-cli 0.7.3 → 0.7.5
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 +39 -7
- 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/src/shared/types.d.ts +1 -0
- package/dist/src/shared/types.d.ts.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-4cd139bb21a2bfd2.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-3064c98adfeef88c.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/{astVwZfaNwQEC0C3qtVHe → H5hpAyftmmiG3FRvwixYw}/_buildManifest.js +0 -0
- /package/dist/web/_next/static/{astVwZfaNwQEC0C3qtVHe → H5hpAyftmmiG3FRvwixYw}/_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
|
|
@@ -1166,8 +1194,10 @@ export async function handleApiRequest(req, res, context) {
|
|
|
1166
1194
|
const hasEmployees = fs.existsSync(ORG_DIR) &&
|
|
1167
1195
|
fs.readdirSync(ORG_DIR, { recursive: true }).some((f) => String(f).endsWith(".yaml") && !String(f).endsWith("department.yaml"));
|
|
1168
1196
|
const config = context.getConfig();
|
|
1197
|
+
const onboarded = config.portal?.onboarded === true;
|
|
1169
1198
|
return json(res, {
|
|
1170
|
-
needed: sessions.length === 0 && !hasEmployees,
|
|
1199
|
+
needed: !onboarded && sessions.length === 0 && !hasEmployees,
|
|
1200
|
+
onboarded,
|
|
1171
1201
|
sessionsCount: sessions.length,
|
|
1172
1202
|
hasEmployees,
|
|
1173
1203
|
portalName: config.portal?.portalName ?? null,
|
|
@@ -1188,6 +1218,7 @@ export async function handleApiRequest(req, res, context) {
|
|
|
1188
1218
|
...config,
|
|
1189
1219
|
portal: {
|
|
1190
1220
|
...config.portal,
|
|
1221
|
+
onboarded: true,
|
|
1191
1222
|
...(portalName !== undefined && { portalName: portalName || undefined }),
|
|
1192
1223
|
...(operatorName !== undefined && { operatorName: operatorName || undefined }),
|
|
1193
1224
|
...(language !== undefined && { language: language || undefined }),
|
|
@@ -1619,7 +1650,7 @@ function loadTranscriptMessages(engineSessionId) {
|
|
|
1619
1650
|
}
|
|
1620
1651
|
return [];
|
|
1621
1652
|
}
|
|
1622
|
-
async function runWebSession(session, prompt, engine, config, context) {
|
|
1653
|
+
async function runWebSession(session, prompt, engine, config, context, attachments) {
|
|
1623
1654
|
const currentSession = getSession(session.id);
|
|
1624
1655
|
if (!currentSession) {
|
|
1625
1656
|
logger.info(`Skipping deleted web session ${session.id} before run start`);
|
|
@@ -1684,6 +1715,7 @@ async function runWebSession(session, prompt, engine, config, context) {
|
|
|
1684
1715
|
model: currentSession.model ?? engineConfig.model,
|
|
1685
1716
|
effortLevel,
|
|
1686
1717
|
cliFlags: employee?.cliFlags,
|
|
1718
|
+
attachments: attachments?.length ? attachments : undefined,
|
|
1687
1719
|
sessionId: currentSession.id,
|
|
1688
1720
|
onStream: (delta) => {
|
|
1689
1721
|
const now = Date.now();
|