wikimem 0.8.0 → 0.8.2

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 (113) hide show
  1. package/dist/cli/commands/init.d.ts.map +1 -1
  2. package/dist/cli/commands/init.js +97 -8
  3. package/dist/cli/commands/init.js.map +1 -1
  4. package/dist/core/connectors.d.ts +1 -1
  5. package/dist/core/connectors.d.ts.map +1 -1
  6. package/dist/core/git.d.ts +1 -1
  7. package/dist/core/git.d.ts.map +1 -1
  8. package/dist/core/git.js.map +1 -1
  9. package/dist/core/ingest.d.ts.map +1 -1
  10. package/dist/core/ingest.js +74 -3
  11. package/dist/core/ingest.js.map +1 -1
  12. package/dist/core/lint.d.ts.map +1 -1
  13. package/dist/core/lint.js +23 -4
  14. package/dist/core/lint.js.map +1 -1
  15. package/dist/core/oauth-defaults.d.ts +31 -0
  16. package/dist/core/oauth-defaults.d.ts.map +1 -0
  17. package/dist/core/oauth-defaults.js +79 -0
  18. package/dist/core/oauth-defaults.js.map +1 -0
  19. package/dist/core/observer.d.ts +24 -1
  20. package/dist/core/observer.d.ts.map +1 -1
  21. package/dist/core/observer.js +146 -4
  22. package/dist/core/observer.js.map +1 -1
  23. package/dist/core/sync/gdrive.d.ts +14 -0
  24. package/dist/core/sync/gdrive.d.ts.map +1 -0
  25. package/dist/core/sync/gdrive.js +205 -0
  26. package/dist/core/sync/gdrive.js.map +1 -0
  27. package/dist/core/sync/github.d.ts +20 -0
  28. package/dist/core/sync/github.d.ts.map +1 -0
  29. package/dist/core/sync/github.js +206 -0
  30. package/dist/core/sync/github.js.map +1 -0
  31. package/dist/core/sync/gmail.d.ts +15 -0
  32. package/dist/core/sync/gmail.d.ts.map +1 -0
  33. package/dist/core/sync/gmail.js +159 -0
  34. package/dist/core/sync/gmail.js.map +1 -0
  35. package/dist/core/sync/index.d.ts +47 -0
  36. package/dist/core/sync/index.d.ts.map +1 -0
  37. package/dist/core/sync/index.js +100 -0
  38. package/dist/core/sync/index.js.map +1 -0
  39. package/dist/core/sync/jira.d.ts +15 -0
  40. package/dist/core/sync/jira.d.ts.map +1 -0
  41. package/dist/core/sync/jira.js +176 -0
  42. package/dist/core/sync/jira.js.map +1 -0
  43. package/dist/core/sync/linear.d.ts +15 -0
  44. package/dist/core/sync/linear.d.ts.map +1 -0
  45. package/dist/core/sync/linear.js +111 -0
  46. package/dist/core/sync/linear.js.map +1 -0
  47. package/dist/core/sync/notion.d.ts +14 -0
  48. package/dist/core/sync/notion.d.ts.map +1 -0
  49. package/dist/core/sync/notion.js +168 -0
  50. package/dist/core/sync/notion.js.map +1 -0
  51. package/dist/core/sync/rss.d.ts +20 -0
  52. package/dist/core/sync/rss.d.ts.map +1 -0
  53. package/dist/core/sync/rss.js +165 -0
  54. package/dist/core/sync/rss.js.map +1 -0
  55. package/dist/core/sync/scheduler.d.ts +31 -0
  56. package/dist/core/sync/scheduler.d.ts.map +1 -0
  57. package/dist/core/sync/scheduler.js +129 -0
  58. package/dist/core/sync/scheduler.js.map +1 -0
  59. package/dist/core/sync/slack.d.ts +16 -0
  60. package/dist/core/sync/slack.d.ts.map +1 -0
  61. package/dist/core/sync/slack.js +173 -0
  62. package/dist/core/sync/slack.js.map +1 -0
  63. package/dist/core/vault.d.ts +22 -0
  64. package/dist/core/vault.d.ts.map +1 -1
  65. package/dist/core/vault.js +65 -0
  66. package/dist/core/vault.js.map +1 -1
  67. package/dist/core/webhooks.d.ts +13 -0
  68. package/dist/core/webhooks.d.ts.map +1 -0
  69. package/dist/core/webhooks.js +206 -0
  70. package/dist/core/webhooks.js.map +1 -0
  71. package/dist/mcp-server.d.ts +11 -6
  72. package/dist/mcp-server.d.ts.map +1 -1
  73. package/dist/mcp-server.js +99 -6
  74. package/dist/mcp-server.js.map +1 -1
  75. package/dist/mcp-tools-extended.d.ts +15 -0
  76. package/dist/mcp-tools-extended.d.ts.map +1 -0
  77. package/dist/mcp-tools-extended.js +277 -0
  78. package/dist/mcp-tools-extended.js.map +1 -0
  79. package/dist/processors/csv.d.ts +18 -0
  80. package/dist/processors/csv.d.ts.map +1 -0
  81. package/dist/processors/csv.js +230 -0
  82. package/dist/processors/csv.js.map +1 -0
  83. package/dist/processors/image.d.ts.map +1 -1
  84. package/dist/processors/image.js +55 -27
  85. package/dist/processors/image.js.map +1 -1
  86. package/dist/processors/pdf.d.ts.map +1 -1
  87. package/dist/processors/pdf.js +5 -1
  88. package/dist/processors/pdf.js.map +1 -1
  89. package/dist/processors/pptx.d.ts +3 -1
  90. package/dist/processors/pptx.d.ts.map +1 -1
  91. package/dist/processors/pptx.js +236 -95
  92. package/dist/processors/pptx.js.map +1 -1
  93. package/dist/processors/xlsx.d.ts +2 -0
  94. package/dist/processors/xlsx.d.ts.map +1 -1
  95. package/dist/processors/xlsx.js +182 -46
  96. package/dist/processors/xlsx.js.map +1 -1
  97. package/dist/templates/source-types.d.ts +33 -0
  98. package/dist/templates/source-types.d.ts.map +1 -0
  99. package/dist/templates/source-types.js +178 -0
  100. package/dist/templates/source-types.js.map +1 -0
  101. package/dist/web/public/index.html +1785 -103
  102. package/dist/web/server.d.ts.map +1 -1
  103. package/dist/web/server.js +746 -38
  104. package/dist/web/server.js.map +1 -1
  105. package/package.json +4 -1
  106. package/src/web/public/index.html +1785 -103
  107. package/templates/source-types/article.md +21 -0
  108. package/templates/source-types/book.md +21 -0
  109. package/templates/source-types/paper.md +23 -0
  110. package/templates/source-types/podcast.md +21 -0
  111. package/templates/source-types/raw-notes.md +17 -0
  112. package/templates/source-types/tweet-thread.md +19 -0
  113. package/templates/source-types/video.md +21 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gdrive.d.ts","sourceRoot":"","sources":["../../../src/core/sync/gdrive.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AA+LD,wBAAsB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAmExF"}
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Google Drive sync module — fetches recent files and exports Google Workspace
3
+ * documents as markdown wiki pages under wiki/sources/.
4
+ */
5
+ import { mkdirSync, writeFileSync, existsSync } from 'node:fs';
6
+ import { join } from 'node:path';
7
+ const API = 'https://www.googleapis.com/drive/v3';
8
+ /** Google Workspace MIME types we can export as text */
9
+ const EXPORT_MAP = {
10
+ 'application/vnd.google-apps.document': { mimeType: 'text/plain', label: 'Google Doc' },
11
+ 'application/vnd.google-apps.spreadsheet': { mimeType: 'text/csv', label: 'Google Sheet' },
12
+ 'application/vnd.google-apps.presentation': { mimeType: 'text/plain', label: 'Google Slides' },
13
+ };
14
+ /** MIME types we can download directly as text */
15
+ const TEXT_MIME_TYPES = new Set([
16
+ 'text/plain',
17
+ 'text/markdown',
18
+ 'text/csv',
19
+ 'text/html',
20
+ 'application/json',
21
+ 'text/x-python',
22
+ 'text/javascript',
23
+ 'application/x-yaml',
24
+ ]);
25
+ function sanitizeFilename(raw) {
26
+ return (raw
27
+ .toLowerCase()
28
+ .replace(/[^a-z0-9]+/g, '-')
29
+ .replace(/-+/g, '-')
30
+ .replace(/^-|-$/g, '')
31
+ .slice(0, 120) || 'untitled');
32
+ }
33
+ function errorHint(status, message) {
34
+ if (status === 401)
35
+ return 'Token expired — re-authenticate with Google OAuth';
36
+ if (status === 403)
37
+ return 'Insufficient permissions — ensure drive.readonly scope';
38
+ if (status === 429)
39
+ return 'Rate limited — try again later';
40
+ return `HTTP ${status}: ${message}`;
41
+ }
42
+ function recentTimestamp() {
43
+ const d = new Date();
44
+ d.setDate(d.getDate() - 30);
45
+ return d.toISOString();
46
+ }
47
+ async function driveFetch(url, token) {
48
+ const res = await fetch(url, {
49
+ headers: { Authorization: `Bearer ${token}` },
50
+ });
51
+ if (!res.ok) {
52
+ const body = await res.text().catch(() => '');
53
+ return { ok: false, status: res.status, message: body };
54
+ }
55
+ return { ok: true, data: (await res.json()) };
56
+ }
57
+ async function driveFetchText(url, token) {
58
+ const res = await fetch(url, {
59
+ headers: { Authorization: `Bearer ${token}` },
60
+ });
61
+ if (!res.ok) {
62
+ const body = await res.text().catch(() => '');
63
+ return { ok: false, status: res.status, message: body };
64
+ }
65
+ return { ok: true, data: await res.text() };
66
+ }
67
+ async function listFiles(token, maxFiles, folderId) {
68
+ const errors = [];
69
+ const allFiles = [];
70
+ let pageToken;
71
+ const since = recentTimestamp();
72
+ do {
73
+ const params = new URLSearchParams({
74
+ orderBy: 'modifiedTime desc',
75
+ pageSize: String(Math.min(50, maxFiles - allFiles.length)),
76
+ fields: 'nextPageToken,files(id,name,mimeType,modifiedTime,webViewLink)',
77
+ });
78
+ const queryParts = [`modifiedTime > '${since}'`, 'trashed = false'];
79
+ if (folderId) {
80
+ queryParts.push(`'${folderId}' in parents`);
81
+ }
82
+ params.set('q', queryParts.join(' and '));
83
+ if (pageToken) {
84
+ params.set('pageToken', pageToken);
85
+ }
86
+ const result = await driveFetch(`${API}/files?${params.toString()}`, token);
87
+ if (!result.ok) {
88
+ errors.push(`listFiles: ${errorHint(result.status, result.message)}`);
89
+ break;
90
+ }
91
+ allFiles.push(...result.data.files);
92
+ pageToken = result.data.nextPageToken;
93
+ } while (pageToken && allFiles.length < maxFiles);
94
+ return { files: allFiles.slice(0, maxFiles), errors };
95
+ }
96
+ async function exportFile(token, fileId, exportMimeType) {
97
+ const result = await driveFetchText(`${API}/files/${fileId}/export?mimeType=${encodeURIComponent(exportMimeType)}`, token);
98
+ if (!result.ok) {
99
+ return { content: null, error: `export(${fileId}): ${errorHint(result.status, result.message)}` };
100
+ }
101
+ return { content: result.data, error: null };
102
+ }
103
+ async function downloadFile(token, fileId) {
104
+ const result = await driveFetchText(`${API}/files/${fileId}?alt=media`, token);
105
+ if (!result.ok) {
106
+ return { content: null, error: `download(${fileId}): ${errorHint(result.status, result.message)}` };
107
+ }
108
+ return { content: result.data, error: null };
109
+ }
110
+ function fileToMarkdown(file, body, sourceLabel) {
111
+ const esc = (s) => s.replace(/'/g, "''");
112
+ const frontmatter = [
113
+ '---',
114
+ `type: source`,
115
+ `source-type: gdrive`,
116
+ `title: '${esc(file.name)}'`,
117
+ `gdrive-id: '${file.id}'`,
118
+ `mime-type: '${file.mimeType}'`,
119
+ `modified: '${file.modifiedTime}'`,
120
+ `created: '${new Date().toISOString()}'`,
121
+ file.webViewLink ? `link: '${file.webViewLink}'` : null,
122
+ `tags: [gdrive, ${sourceLabel.toLowerCase().replace(/\s+/g, '-')}]`,
123
+ '---',
124
+ ]
125
+ .filter(Boolean)
126
+ .join('\n');
127
+ const links = file.webViewLink
128
+ ? `\n## Links\n\n- [Open in Google Drive](${file.webViewLink})\n`
129
+ : '';
130
+ return {
131
+ filename: `gdrive-${sanitizeFilename(file.name)}.md`,
132
+ content: `${frontmatter}\n\n# ${file.name}\n\n> Source: ${sourceLabel} via Google Drive sync\n\n${body.trim()}\n${links}`,
133
+ };
134
+ }
135
+ export async function syncGDrive(options) {
136
+ const start = Date.now();
137
+ const errors = [];
138
+ let filesWritten = 0;
139
+ const maxFiles = options.maxFiles ?? 50;
140
+ const outDir = join(options.vaultRoot, 'wiki', 'sources');
141
+ if (!existsSync(outDir))
142
+ mkdirSync(outDir, { recursive: true });
143
+ const { files, errors: listErrors } = await listFiles(options.token, maxFiles, options.folderId);
144
+ errors.push(...listErrors);
145
+ if (files.length === 0 && listErrors.length > 0) {
146
+ return { provider: 'gdrive', filesWritten: 0, errors, duration: Date.now() - start };
147
+ }
148
+ for (const file of files) {
149
+ const exportInfo = EXPORT_MAP[file.mimeType];
150
+ if (exportInfo) {
151
+ // Google Workspace file — export via /export endpoint
152
+ const { content, error } = await exportFile(options.token, file.id, exportInfo.mimeType);
153
+ if (error) {
154
+ errors.push(error);
155
+ continue;
156
+ }
157
+ if (!content)
158
+ continue;
159
+ const { filename, content: md } = fileToMarkdown(file, content, exportInfo.label);
160
+ try {
161
+ writeFileSync(join(outDir, filename), md, 'utf-8');
162
+ filesWritten++;
163
+ }
164
+ catch (err) {
165
+ const msg = err instanceof Error ? err.message : String(err);
166
+ errors.push(`Write failed (${filename}): ${msg}`);
167
+ }
168
+ }
169
+ else if (TEXT_MIME_TYPES.has(file.mimeType)) {
170
+ // Plain text file — download directly
171
+ const { content, error } = await downloadFile(options.token, file.id);
172
+ if (error) {
173
+ errors.push(error);
174
+ continue;
175
+ }
176
+ if (!content)
177
+ continue;
178
+ const label = file.mimeType.split('/').pop() ?? 'text';
179
+ const { filename, content: md } = fileToMarkdown(file, content, label);
180
+ try {
181
+ writeFileSync(join(outDir, filename), md, 'utf-8');
182
+ filesWritten++;
183
+ }
184
+ catch (err) {
185
+ const msg = err instanceof Error ? err.message : String(err);
186
+ errors.push(`Write failed (${filename}): ${msg}`);
187
+ }
188
+ }
189
+ else {
190
+ // Binary file (PDF, image, etc.) — note existence but skip content
191
+ const body = `_Binary file (${file.mimeType}) — content not exported. Open in Google Drive to view._`;
192
+ const { filename, content: md } = fileToMarkdown(file, body, 'binary');
193
+ try {
194
+ writeFileSync(join(outDir, filename), md, 'utf-8');
195
+ filesWritten++;
196
+ }
197
+ catch (err) {
198
+ const msg = err instanceof Error ? err.message : String(err);
199
+ errors.push(`Write failed (${filename}): ${msg}`);
200
+ }
201
+ }
202
+ }
203
+ return { provider: 'gdrive', filesWritten, errors, duration: Date.now() - start };
204
+ }
205
+ //# sourceMappingURL=gdrive.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gdrive.js","sourceRoot":"","sources":["../../../src/core/sync/gdrive.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * GitHub Sync — fetches repos, issues, PRs, and READMEs into raw/ for wiki ingest.
3
+ * Uses GitHub REST API v3 with OAuth access_token from .wikimem/tokens.json.
4
+ */
5
+ export interface GitHubSyncOptions {
6
+ token: string;
7
+ vaultRoot: string;
8
+ repos?: string[];
9
+ maxRepos?: number;
10
+ maxIssuesPerRepo?: number;
11
+ maxPRsPerRepo?: number;
12
+ }
13
+ export interface PlatformSyncResult {
14
+ provider: string;
15
+ filesWritten: number;
16
+ errors: string[];
17
+ duration: number;
18
+ }
19
+ export declare function syncGitHub(options: GitHubSyncOptions): Promise<PlatformSyncResult>;
20
+ //# sourceMappingURL=github.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../../../src/core/sync/github.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AA6JD,wBAAsB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAqGxF"}
@@ -0,0 +1,206 @@
1
+ /**
2
+ * GitHub Sync — fetches repos, issues, PRs, and READMEs into raw/ for wiki ingest.
3
+ * Uses GitHub REST API v3 with OAuth access_token from .wikimem/tokens.json.
4
+ */
5
+ import { mkdirSync, writeFileSync } from 'node:fs';
6
+ import { join } from 'node:path';
7
+ const API_BASE = 'https://api.github.com';
8
+ const MAX_TOTAL_CALLS = 200;
9
+ let callCount = 0;
10
+ async function ghFetch(path, token) {
11
+ if (callCount >= MAX_TOTAL_CALLS) {
12
+ return { data: null, error: `API call cap reached (${MAX_TOTAL_CALLS})` };
13
+ }
14
+ callCount++;
15
+ try {
16
+ const res = await fetch(`${API_BASE}${path}`, {
17
+ headers: {
18
+ Authorization: `Bearer ${token}`,
19
+ Accept: 'application/vnd.github.v3+json',
20
+ 'User-Agent': 'wikimem-sync',
21
+ },
22
+ });
23
+ const remaining = res.headers.get('X-RateLimit-Remaining');
24
+ if (remaining !== null && parseInt(remaining, 10) < 10) {
25
+ const resetAt = res.headers.get('X-RateLimit-Reset');
26
+ const resetTime = resetAt ? new Date(parseInt(resetAt, 10) * 1000).toISOString() : 'unknown';
27
+ return { data: null, error: `Rate limit nearly exhausted (${remaining} left, resets ${resetTime})` };
28
+ }
29
+ if (!res.ok) {
30
+ return { data: null, error: `GitHub API ${res.status}: ${res.statusText} for ${path}` };
31
+ }
32
+ const data = (await res.json());
33
+ return { data, error: null };
34
+ }
35
+ catch (err) {
36
+ const msg = err instanceof Error ? err.message : String(err);
37
+ return { data: null, error: `Fetch failed for ${path}: ${msg}` };
38
+ }
39
+ }
40
+ function todayDate() {
41
+ return new Date().toISOString().slice(0, 10);
42
+ }
43
+ function frontmatter(fields) {
44
+ const lines = ['---'];
45
+ for (const [key, val] of Object.entries(fields)) {
46
+ if (Array.isArray(val)) {
47
+ lines.push(`${key}: [${val.map((v) => `"${v}"`).join(', ')}]`);
48
+ }
49
+ else if (typeof val === 'string') {
50
+ lines.push(`${key}: "${val.replace(/"/g, '\\"')}"`);
51
+ }
52
+ else {
53
+ lines.push(`${key}: ${String(val)}`);
54
+ }
55
+ }
56
+ lines.push('---');
57
+ return lines.join('\n');
58
+ }
59
+ function sanitizeFilename(name) {
60
+ return name.replace(/[^a-zA-Z0-9._-]/g, '-').slice(0, 100);
61
+ }
62
+ function writeMarkdown(dir, filename, content) {
63
+ mkdirSync(dir, { recursive: true });
64
+ writeFileSync(join(dir, filename), content, 'utf-8');
65
+ }
66
+ function buildRepoPage(repo) {
67
+ const fm = frontmatter({
68
+ title: repo.full_name, addedBy: 'connector', source: 'github', type: 'repository',
69
+ url: repo.html_url, language: repo.language ?? 'unknown',
70
+ stars: repo.stargazers_count, forks: repo.forks_count, syncedAt: new Date().toISOString(),
71
+ });
72
+ const topics = repo.topics.length > 0 ? `\n**Topics:** ${repo.topics.join(', ')}` : '';
73
+ return `${fm}\n\n# ${repo.full_name}\n\n${repo.description ?? '_No description._'}\n
74
+ | Stat | Value |
75
+ |------|-------|
76
+ | Language | ${repo.language ?? 'N/A'} |
77
+ | Stars | ${repo.stargazers_count} |
78
+ | Forks | ${repo.forks_count} |
79
+ | Open Issues | ${repo.open_issues_count} |
80
+ | Updated | ${repo.updated_at} |
81
+ | Private | ${repo.private ? 'Yes' : 'No'} |
82
+ ${topics}\n\n[View on GitHub](${repo.html_url})\n`;
83
+ }
84
+ function buildIssuePage(repo, issue) {
85
+ const fm = frontmatter({
86
+ title: `#${issue.number} ${issue.title}`, addedBy: 'connector', source: 'github',
87
+ type: 'issue', repo, url: issue.html_url, state: issue.state,
88
+ author: issue.user?.login ?? 'unknown', labels: issue.labels.map((l) => l.name),
89
+ createdAt: issue.created_at, syncedAt: new Date().toISOString(),
90
+ });
91
+ const body = issue.body ? issue.body.slice(0, 5000) : '_No description._';
92
+ return `${fm}\n\n# ${issue.title}\n\n**Issue #${issue.number}** in \`${repo}\` | ${issue.state} | by ${issue.user?.login ?? 'unknown'}\n\n${body}\n`;
93
+ }
94
+ function buildPRPage(repo, pr) {
95
+ const fm = frontmatter({
96
+ title: `PR #${pr.number} ${pr.title}`, addedBy: 'connector', source: 'github',
97
+ type: 'pull-request', repo, url: pr.html_url, state: pr.state,
98
+ author: pr.user?.login ?? 'unknown', branch: `${pr.head.ref} -> ${pr.base.ref}`,
99
+ createdAt: pr.created_at, syncedAt: new Date().toISOString(),
100
+ });
101
+ const body = pr.body ? pr.body.slice(0, 5000) : '_No description._';
102
+ return `${fm}\n\n# ${pr.title}\n\n**PR #${pr.number}** in \`${repo}\` | ${pr.state} | \`${pr.head.ref}\` -> \`${pr.base.ref}\` | by ${pr.user?.login ?? 'unknown'}\n\n${body}\n`;
103
+ }
104
+ function buildReadmePage(repo, content, htmlUrl) {
105
+ const fm = frontmatter({
106
+ title: `${repo} README`, addedBy: 'connector', source: 'github',
107
+ type: 'readme', repo, url: htmlUrl, syncedAt: new Date().toISOString(),
108
+ });
109
+ return `${fm}\n\n${content.slice(0, 10000)}\n`;
110
+ }
111
+ export async function syncGitHub(options) {
112
+ const start = Date.now();
113
+ callCount = 0;
114
+ const errors = [];
115
+ let filesWritten = 0;
116
+ const date = todayDate();
117
+ const outDir = join(options.vaultRoot, 'raw', date);
118
+ mkdirSync(outDir, { recursive: true });
119
+ const maxRepos = options.maxRepos ?? 30;
120
+ const maxIssues = options.maxIssuesPerRepo ?? 20;
121
+ const maxPRs = options.maxPRsPerRepo ?? 10;
122
+ // Step 1: Get repos
123
+ let repos = [];
124
+ if (options.repos && options.repos.length > 0) {
125
+ for (const fullName of options.repos.slice(0, maxRepos)) {
126
+ const { data, error } = await ghFetch(`/repos/${fullName}`, options.token);
127
+ if (error) {
128
+ errors.push(error);
129
+ continue;
130
+ }
131
+ if (data)
132
+ repos.push(data);
133
+ }
134
+ }
135
+ else {
136
+ const { data, error } = await ghFetch(`/user/repos?sort=updated&per_page=${maxRepos}&type=owner`, options.token);
137
+ if (error)
138
+ errors.push(error);
139
+ if (data)
140
+ repos = data;
141
+ }
142
+ // Step 2: Write repo summaries and fetch details
143
+ for (const repo of repos) {
144
+ const repoDir = join(outDir, 'github', sanitizeFilename(repo.full_name));
145
+ // Repo summary page
146
+ writeMarkdown(repoDir, 'repo.md', buildRepoPage(repo));
147
+ filesWritten++;
148
+ // README
149
+ const { data: readmeData, error: readmeErr } = await ghFetch(`/repos/${repo.full_name}/readme`, options.token);
150
+ if (readmeErr) {
151
+ if (!readmeErr.includes('404'))
152
+ errors.push(readmeErr);
153
+ }
154
+ else if (readmeData) {
155
+ try {
156
+ const decoded = Buffer.from(readmeData.content, 'base64').toString('utf-8');
157
+ writeMarkdown(repoDir, 'README.md', buildReadmePage(repo.full_name, decoded, readmeData.html_url));
158
+ filesWritten++;
159
+ }
160
+ catch (decodeErr) {
161
+ errors.push(`Failed to decode README for ${repo.full_name}`);
162
+ }
163
+ }
164
+ // Issues
165
+ if (repo.open_issues_count > 0) {
166
+ const { data: issues, error: issueErr } = await ghFetch(`/repos/${repo.full_name}/issues?state=open&per_page=${maxIssues}&sort=updated`, options.token);
167
+ if (issueErr) {
168
+ errors.push(issueErr);
169
+ }
170
+ if (issues) {
171
+ const issueDir = join(repoDir, 'issues');
172
+ for (const issue of issues) {
173
+ // The issues endpoint includes PRs; skip them
174
+ if ('pull_request' in issue)
175
+ continue;
176
+ writeMarkdown(issueDir, `issue-${issue.number}.md`, buildIssuePage(repo.full_name, issue));
177
+ filesWritten++;
178
+ }
179
+ }
180
+ }
181
+ // PRs
182
+ const { data: prs, error: prErr } = await ghFetch(`/repos/${repo.full_name}/pulls?state=open&per_page=${maxPRs}&sort=updated`, options.token);
183
+ if (prErr) {
184
+ errors.push(prErr);
185
+ }
186
+ if (prs) {
187
+ const prDir = join(repoDir, 'prs');
188
+ for (const pr of prs) {
189
+ writeMarkdown(prDir, `pr-${pr.number}.md`, buildPRPage(repo.full_name, pr));
190
+ filesWritten++;
191
+ }
192
+ }
193
+ // Bail early if hitting call cap
194
+ if (callCount >= MAX_TOTAL_CALLS) {
195
+ errors.push(`Stopped after ${repos.indexOf(repo) + 1} repos — API call cap reached`);
196
+ break;
197
+ }
198
+ }
199
+ return {
200
+ provider: 'github',
201
+ filesWritten,
202
+ errors,
203
+ duration: Date.now() - start,
204
+ };
205
+ }
206
+ //# sourceMappingURL=github.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.js","sourceRoot":"","sources":["../../../src/core/sync/github.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ export interface GmailSyncOptions {
2
+ token: string;
3
+ vaultRoot: string;
4
+ maxThreads?: number;
5
+ query?: string;
6
+ labelIds?: string[];
7
+ }
8
+ export interface PlatformSyncResult {
9
+ provider: string;
10
+ filesWritten: number;
11
+ errors: string[];
12
+ duration: number;
13
+ }
14
+ export declare function syncGmail(options: GmailSyncOptions): Promise<PlatformSyncResult>;
15
+ //# sourceMappingURL=gmail.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gmail.d.ts","sourceRoot":"","sources":["../../../src/core/sync/gmail.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AA8KD,wBAAsB,SAAS,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAkCtF"}
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Gmail sync module — fetches recent threads and writes them as markdown
3
+ * into the wiki vault's raw/{date}/ directory.
4
+ */
5
+ import { mkdirSync, writeFileSync, existsSync } from 'node:fs';
6
+ import { join } from 'node:path';
7
+ const API = 'https://gmail.googleapis.com/gmail/v1/users/me';
8
+ function getHeader(headers, name) {
9
+ return headers.find((h) => h.name.toLowerCase() === name.toLowerCase())?.value ?? '';
10
+ }
11
+ function decode64(data) {
12
+ return Buffer.from(data, 'base64url').toString('utf-8');
13
+ }
14
+ function extractPlainText(part) {
15
+ if (part.mimeType === 'text/plain' && part.body.data)
16
+ return decode64(part.body.data);
17
+ if (part.parts) {
18
+ for (const sub of part.parts) {
19
+ const text = extractPlainText(sub);
20
+ if (text)
21
+ return text;
22
+ }
23
+ }
24
+ return '';
25
+ }
26
+ function extractBody(msg) {
27
+ const { payload } = msg;
28
+ if (payload.body.data)
29
+ return decode64(payload.body.data);
30
+ if (payload.parts) {
31
+ const plain = payload.parts.find((p) => p.mimeType === 'text/plain');
32
+ if (plain?.body.data)
33
+ return decode64(plain.body.data);
34
+ for (const part of payload.parts) {
35
+ const text = extractPlainText(part);
36
+ if (text)
37
+ return text;
38
+ }
39
+ const html = payload.parts.find((p) => p.mimeType === 'text/html');
40
+ if (html?.body.data)
41
+ return decode64(html.body.data);
42
+ }
43
+ return '(no body content)';
44
+ }
45
+ function sanitizeFilename(raw) {
46
+ return (raw.replace(/[/\\:*?"<>|]/g, '-').replace(/\s+/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '').slice(0, 120) ||
47
+ 'untitled');
48
+ }
49
+ function formatDate(internalDate) {
50
+ const ms = parseInt(internalDate, 10);
51
+ return Number.isNaN(ms) ? new Date().toISOString() : new Date(ms).toISOString();
52
+ }
53
+ function todayStr() {
54
+ const n = new Date();
55
+ return `${n.getFullYear()}-${String(n.getMonth() + 1).padStart(2, '0')}-${String(n.getDate()).padStart(2, '0')}`;
56
+ }
57
+ function errorHint(status, message) {
58
+ if (status === 401)
59
+ return 'Token expired — re-authenticate with Google OAuth';
60
+ if (status === 429)
61
+ return 'Rate limited — try again later';
62
+ return `HTTP ${status}: ${message}`;
63
+ }
64
+ async function gmailFetch(endpoint, token) {
65
+ const res = await fetch(`${API}${endpoint}`, {
66
+ headers: { Authorization: `Bearer ${token}` },
67
+ });
68
+ if (!res.ok) {
69
+ const body = await res.text().catch(() => '');
70
+ return { ok: false, status: res.status, message: body };
71
+ }
72
+ return { ok: true, data: (await res.json()) };
73
+ }
74
+ async function listThreads(token, maxResults, query, labelIds) {
75
+ const params = new URLSearchParams({ maxResults: String(maxResults) });
76
+ if (query)
77
+ params.set('q', query);
78
+ if (labelIds?.length) {
79
+ for (const lid of labelIds)
80
+ params.append('labelIds', lid);
81
+ }
82
+ const result = await gmailFetch(`/threads?${params.toString()}`, token);
83
+ if (result.ok === false) {
84
+ return { threads: [], errors: [`listThreads: ${errorHint(result.status, result.message)}`] };
85
+ }
86
+ return { threads: result.data.threads ?? [], errors: [] };
87
+ }
88
+ async function getThread(token, threadId) {
89
+ const result = await gmailFetch(`/threads/${threadId}?format=full`, token);
90
+ if (result.ok === false) {
91
+ return { thread: null, error: `getThread(${threadId}): ${errorHint(result.status, result.message)}` };
92
+ }
93
+ return { thread: result.data, error: null };
94
+ }
95
+ function threadToMarkdown(thread) {
96
+ const messages = thread.messages ?? [];
97
+ const firstMsg = messages[0];
98
+ const headers = firstMsg?.payload.headers ?? [];
99
+ const subject = getHeader(headers, 'Subject') || '(no subject)';
100
+ const from = getHeader(headers, 'From');
101
+ const date = firstMsg ? formatDate(firstMsg.internalDate) : new Date().toISOString();
102
+ const esc = (s) => s.replace(/'/g, "''");
103
+ const frontmatter = [
104
+ '---',
105
+ `addedBy: 'connector'`,
106
+ `source: 'gmail'`,
107
+ `subject: '${esc(subject)}'`,
108
+ `from: '${esc(from)}'`,
109
+ `date: '${date}'`,
110
+ `threadId: '${thread.id}'`,
111
+ `messageCount: ${messages.length}`,
112
+ '---',
113
+ ].join('\n');
114
+ const body = messages
115
+ .map((msg) => {
116
+ const mFrom = getHeader(msg.payload.headers, 'From');
117
+ const mDate = formatDate(msg.internalDate);
118
+ return `## From: ${mFrom}\n_${mDate}_\n\n${extractBody(msg).trim()}`;
119
+ })
120
+ .join('\n\n---\n\n');
121
+ return {
122
+ filename: `gmail-${sanitizeFilename(subject)}.md`,
123
+ content: `${frontmatter}\n\n# ${subject}\n\n${body}\n`,
124
+ };
125
+ }
126
+ export async function syncGmail(options) {
127
+ const start = Date.now();
128
+ const errors = [];
129
+ let filesWritten = 0;
130
+ const maxThreads = options.maxThreads ?? 50;
131
+ const outDir = join(options.vaultRoot, 'raw', todayStr());
132
+ if (!existsSync(outDir))
133
+ mkdirSync(outDir, { recursive: true });
134
+ const { threads, errors: listErrors } = await listThreads(options.token, maxThreads, options.query, options.labelIds);
135
+ errors.push(...listErrors);
136
+ if (threads.length === 0 && listErrors.length > 0) {
137
+ return { provider: 'gmail', filesWritten: 0, errors, duration: Date.now() - start };
138
+ }
139
+ for (const stub of threads) {
140
+ const { thread, error } = await getThread(options.token, stub.id);
141
+ if (error) {
142
+ errors.push(error);
143
+ continue;
144
+ }
145
+ if (!thread)
146
+ continue;
147
+ const { filename, content } = threadToMarkdown(thread);
148
+ try {
149
+ writeFileSync(join(outDir, filename), content, 'utf-8');
150
+ filesWritten++;
151
+ }
152
+ catch (err) {
153
+ const msg = err instanceof Error ? err.message : String(err);
154
+ errors.push(`Write failed (${filename}): ${msg}`);
155
+ }
156
+ }
157
+ return { provider: 'gmail', filesWritten, errors, duration: Date.now() - start };
158
+ }
159
+ //# sourceMappingURL=gmail.js.map