openwriter 0.15.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/client/assets/index-CbSQ8xxn.css +1 -0
  2. package/dist/client/assets/{index-B5MXw2pg.js → index-JMMJM_G_.js} +53 -53
  3. package/dist/client/index.html +2 -2
  4. package/dist/plugins/authors-voice/dist/index.d.ts +41 -0
  5. package/dist/plugins/authors-voice/dist/index.js +206 -0
  6. package/dist/plugins/authors-voice/package.json +23 -0
  7. package/dist/plugins/image-gen/dist/index.d.ts +35 -0
  8. package/dist/plugins/image-gen/dist/index.js +141 -0
  9. package/dist/plugins/image-gen/package.json +26 -0
  10. package/dist/plugins/publish/dist/helpers.d.ts +66 -0
  11. package/dist/plugins/publish/dist/helpers.js +199 -0
  12. package/dist/plugins/publish/dist/index.d.ts +3 -0
  13. package/dist/plugins/publish/dist/index.js +1130 -0
  14. package/dist/plugins/publish/dist/newsletter-tools.d.ts +2 -0
  15. package/dist/plugins/publish/dist/newsletter-tools.js +394 -0
  16. package/dist/plugins/publish/package.json +31 -0
  17. package/dist/plugins/x-api/dist/index.d.ts +27 -0
  18. package/dist/plugins/x-api/dist/index.js +240 -0
  19. package/dist/plugins/x-api/package.json +27 -0
  20. package/dist/server/documents.js +234 -3
  21. package/dist/server/enrichment.js +114 -0
  22. package/dist/server/install-skill.js +15 -0
  23. package/dist/server/markdown-parse.js +71 -14
  24. package/dist/server/markdown-serialize.js +14 -16
  25. package/dist/server/mcp.js +250 -23
  26. package/dist/server/node-fingerprint.js +347 -73
  27. package/dist/server/node-matcher.js +19 -44
  28. package/dist/server/pending-overlay.js +21 -4
  29. package/dist/server/state.js +203 -26
  30. package/dist/server/workspaces.js +27 -5
  31. package/dist/server/ws.js +10 -0
  32. package/package.json +1 -1
  33. package/skill/SKILL.md +26 -7
  34. package/skill/agents/openwriter-enrichment-minion.md +184 -0
  35. package/skill/docs/enrichment.md +179 -0
  36. package/dist/client/assets/index-B3iORmCT.css +0 -1
@@ -0,0 +1,199 @@
1
+ import MarkdownIt from 'markdown-it';
2
+ import markdownItIns from 'markdown-it-ins';
3
+ import markdownItMark from 'markdown-it-mark';
4
+ import markdownItSub from 'markdown-it-sub';
5
+ import markdownItSup from 'markdown-it-sup';
6
+ import { readFileSync, existsSync } from 'fs';
7
+ import { join, extname } from 'path';
8
+ // Lazy-load server modules at runtime
9
+ // npm package: dist/plugins/publish/dist/helpers.js → ../../../server/
10
+ // Monorepo: plugins/publish/dist/helpers.js → ../../../packages/openwriter/dist/server/
11
+ const npmBase = new URL('../../../server/', import.meta.url).href;
12
+ const monoBase = new URL('../../../packages/openwriter/dist/server/', import.meta.url).href;
13
+ let _cached = null;
14
+ async function tryImport(base) {
15
+ const [markdown, state, helpers, connections] = await Promise.all([
16
+ import(base + 'markdown.js'),
17
+ import(base + 'state.js'),
18
+ import(base + 'helpers.js'),
19
+ import(base + 'connections.js'),
20
+ ]);
21
+ return { markdown, state, helpers, connections };
22
+ }
23
+ export async function getServerModules() {
24
+ if (_cached)
25
+ return _cached;
26
+ // Try npm package layout first, fall back to monorepo layout
27
+ let markdown, state, helpers, connections;
28
+ try {
29
+ ({ markdown, state, helpers, connections } = await tryImport(npmBase));
30
+ }
31
+ catch {
32
+ ({ markdown, state, helpers, connections } = await tryImport(monoBase));
33
+ }
34
+ _cached = {
35
+ tiptapToMarkdown: markdown.tiptapToMarkdown,
36
+ getDocument: state.getDocument,
37
+ getTitle: state.getTitle,
38
+ getMetadata: state.getMetadata,
39
+ getActiveProfile: helpers.getActiveProfile,
40
+ getDataDir: helpers.getDataDir,
41
+ getDocId: state.getDocId,
42
+ platformFetch: connections.platformFetch,
43
+ };
44
+ return _cached;
45
+ }
46
+ // markdown-it instance matching export-routes.ts configuration
47
+ export const md = new MarkdownIt({ linkify: false, html: true });
48
+ md.enable('strikethrough');
49
+ md.use(markdownItIns);
50
+ md.use(markdownItMark);
51
+ md.use(markdownItSub);
52
+ md.use(markdownItSup);
53
+ /** Strip YAML frontmatter and TipTap empty markers from markdown output */
54
+ export function stripFrontmatter(markdown) {
55
+ let result = markdown;
56
+ const fmMatch = result.match(/^---\n[\s\S]*?\n---\n\n/);
57
+ if (fmMatch)
58
+ result = result.slice(fmMatch[0].length);
59
+ result = result.replace(/^\s*<!--\s*-->\s*$/gm, '');
60
+ return result.trim();
61
+ }
62
+ /** Scan HTML for /_images/ references, read local files, return base64 array for R2 upload */
63
+ export async function extractLocalImages(html) {
64
+ const server = await getServerModules();
65
+ const dataDir = server.getDataDir();
66
+ const images = [];
67
+ const regex = /\/_images\/[^\s"'<>]+/g;
68
+ const seen = new Set();
69
+ let match;
70
+ while ((match = regex.exec(html)) !== null) {
71
+ const imgPath = match[0];
72
+ if (seen.has(imgPath))
73
+ continue;
74
+ seen.add(imgPath);
75
+ const localFile = join(dataDir, imgPath);
76
+ if (!existsSync(localFile))
77
+ continue;
78
+ const data = readFileSync(localFile).toString('base64');
79
+ const ext = extname(imgPath).toLowerCase();
80
+ const mimeMap = {
81
+ '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg',
82
+ '.png': 'image/png', '.gif': 'image/gif',
83
+ '.webp': 'image/webp', '.svg': 'image/svg+xml',
84
+ };
85
+ images.push({ path: imgPath, data, content_type: mimeMap[ext] || 'image/png' });
86
+ }
87
+ return images;
88
+ }
89
+ /** Convert current document's TipTap JSON to body HTML + plain text */
90
+ export async function documentToEmail() {
91
+ const server = await getServerModules();
92
+ const doc = server.getDocument();
93
+ const title = server.getTitle();
94
+ const metadata = server.getMetadata();
95
+ const raw = server.tiptapToMarkdown(doc, title, metadata);
96
+ const clean = stripFrontmatter(raw).trim();
97
+ const html = md.render(clean);
98
+ const text = markdownToPlainText(clean);
99
+ return { html, text, subject: title, json: doc };
100
+ }
101
+ /** Strip markdown syntax to produce clean plain text for email */
102
+ export function markdownToPlainText(markdown) {
103
+ const lines = markdown.split('\n');
104
+ const out = [];
105
+ let inCodeBlock = false;
106
+ let codeLines = [];
107
+ for (const line of lines) {
108
+ if (/^```/.test(line)) {
109
+ if (inCodeBlock) {
110
+ for (const cl of codeLines)
111
+ out.push(' ' + cl);
112
+ codeLines = [];
113
+ inCodeBlock = false;
114
+ }
115
+ else {
116
+ inCodeBlock = true;
117
+ }
118
+ continue;
119
+ }
120
+ if (inCodeBlock) {
121
+ codeLines.push(line);
122
+ continue;
123
+ }
124
+ if (/^\s*<!--.*-->\s*$/.test(line))
125
+ continue;
126
+ if (/^!\[.*\]\(.*\)\s*$/.test(line))
127
+ continue;
128
+ if (/^\|[\s:|-]+\|\s*$/.test(line))
129
+ continue;
130
+ if (/^\|(.+)\|\s*$/.test(line)) {
131
+ const cells = line
132
+ .slice(1, -1)
133
+ .split('|')
134
+ .map((c) => stripInline(c.trim()));
135
+ out.push(cells.join(' | '));
136
+ continue;
137
+ }
138
+ if (/^[-*_]{3,}\s*$/.test(line)) {
139
+ out.push('---');
140
+ continue;
141
+ }
142
+ const headerMatch = line.match(/^(#{1,6})\s+(.*)/);
143
+ if (headerMatch) {
144
+ out.push(stripInline(headerMatch[2]));
145
+ continue;
146
+ }
147
+ const bqMatch = line.match(/^(>\s?)+(.*)$/);
148
+ if (bqMatch) {
149
+ out.push(stripInline(bqMatch[2]));
150
+ continue;
151
+ }
152
+ const taskMatch = line.match(/^(\s*)[-*+]\s+\[([ xX])\]\s+(.*)/);
153
+ if (taskMatch) {
154
+ const indent = taskMatch[1];
155
+ const check = taskMatch[2] === ' ' ? '[ ]' : '[x]';
156
+ out.push(indent + check + ' ' + stripInline(taskMatch[3]));
157
+ continue;
158
+ }
159
+ const ulMatch = line.match(/^(\s*)[-*+]\s+(.*)/);
160
+ if (ulMatch) {
161
+ out.push(ulMatch[1] + '- ' + stripInline(ulMatch[2]));
162
+ continue;
163
+ }
164
+ const olMatch = line.match(/^(\s*)(\d+)\.\s+(.*)/);
165
+ if (olMatch) {
166
+ out.push(olMatch[1] + olMatch[2] + '. ' + stripInline(olMatch[3]));
167
+ continue;
168
+ }
169
+ out.push(stripInline(line));
170
+ }
171
+ if (inCodeBlock) {
172
+ for (const cl of codeLines)
173
+ out.push(' ' + cl);
174
+ }
175
+ return out
176
+ .join('\n')
177
+ .replace(/\n{3,}/g, '\n\n')
178
+ .trim();
179
+ }
180
+ /** Strip inline markdown marks from a string */
181
+ export function stripInline(text) {
182
+ return text
183
+ .replace(/!\[([^\]]*)\]\([^)]+\)/g, '')
184
+ .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1 ($2)')
185
+ .replace(/`([^`]+)`/g, '$1')
186
+ .replace(/(\*{3}|_{3})(.+?)\1/g, '$2')
187
+ .replace(/(\*{2}|_{2})(.+?)\1/g, '$2')
188
+ .replace(/(\*|_)(.+?)\1/g, '$2')
189
+ .replace(/~~(.+?)~~/g, '$1')
190
+ .replace(/\+\+(.+?)\+\+/g, '$1')
191
+ .replace(/==(.+?)==/g, '$1')
192
+ .replace(/~([^~]+)~/g, '$1')
193
+ .replace(/\^([^^]+)\^/g, '$1');
194
+ }
195
+ /** Make an authenticated request to the Publish API via platform proxy */
196
+ export async function publishFetch(_config, path, options = {}) {
197
+ const server = await getServerModules();
198
+ return server.platformFetch(path, options);
199
+ }
@@ -0,0 +1,3 @@
1
+ import type { OpenWriterPlugin } from './helpers.js';
2
+ declare const plugin: OpenWriterPlugin;
3
+ export default plugin;