@rubytech/create-maxy-code 0.1.140 → 0.1.142

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 (83) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/config/brand.json +1 -1
  3. package/payload/platform/neo4j/schema.cypher +42 -67
  4. package/payload/platform/plugins/.claude-plugin/marketplace.json +5 -0
  5. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-erase.d.ts.map +1 -1
  6. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-erase.js +14 -8
  7. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-erase.js.map +1 -1
  8. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-export.d.ts +1 -1
  9. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-export.d.ts.map +1 -1
  10. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-export.js +10 -7
  11. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-export.js.map +1 -1
  12. package/payload/platform/plugins/docs/references/internals.md +1 -1
  13. package/payload/platform/plugins/docs/references/neo4j.md +3 -3
  14. package/payload/platform/plugins/docs/references/plugins-guide.md +1 -0
  15. package/payload/platform/plugins/email/.claude-plugin/plugin.json +1 -1
  16. package/payload/platform/plugins/email/PLUGIN.md +3 -7
  17. package/payload/platform/plugins/email/mcp/dist/index.js +7 -46
  18. package/payload/platform/plugins/email/mcp/dist/index.js.map +1 -1
  19. package/payload/platform/plugins/email/mcp/dist/lib/credentials.d.ts.map +1 -1
  20. package/payload/platform/plugins/email/mcp/dist/lib/credentials.js +4 -15
  21. package/payload/platform/plugins/email/mcp/dist/lib/credentials.js.map +1 -1
  22. package/payload/platform/plugins/email/mcp/dist/lib/graph.d.ts +51 -43
  23. package/payload/platform/plugins/email/mcp/dist/lib/graph.d.ts.map +1 -1
  24. package/payload/platform/plugins/email/mcp/dist/lib/graph.js +223 -229
  25. package/payload/platform/plugins/email/mcp/dist/lib/graph.js.map +1 -1
  26. package/payload/platform/plugins/email/mcp/dist/scripts/email-fetch.js +7 -4
  27. package/payload/platform/plugins/email/mcp/dist/scripts/email-fetch.js.map +1 -1
  28. package/payload/platform/plugins/email/mcp/dist/tools/email-graph-query.d.ts +14 -4
  29. package/payload/platform/plugins/email/mcp/dist/tools/email-graph-query.d.ts.map +1 -1
  30. package/payload/platform/plugins/email/mcp/dist/tools/email-graph-query.js +69 -84
  31. package/payload/platform/plugins/email/mcp/dist/tools/email-graph-query.js.map +1 -1
  32. package/payload/platform/plugins/email/mcp/dist/tools/email-reply.d.ts.map +1 -1
  33. package/payload/platform/plugins/email/mcp/dist/tools/email-reply.js +49 -64
  34. package/payload/platform/plugins/email/mcp/dist/tools/email-reply.js.map +1 -1
  35. package/payload/platform/plugins/email/mcp/dist/tools/email-setup.d.ts.map +1 -1
  36. package/payload/platform/plugins/email/mcp/dist/tools/email-setup.js +1 -2
  37. package/payload/platform/plugins/email/mcp/dist/tools/email-setup.js.map +1 -1
  38. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.d.ts +1 -0
  39. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.d.ts.map +1 -1
  40. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.js +3 -0
  41. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.js.map +1 -1
  42. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.d.ts +1 -1
  43. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.d.ts.map +1 -1
  44. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.js +1 -0
  45. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.js.map +1 -1
  46. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/x-dm.d.ts +3 -0
  47. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/x-dm.d.ts.map +1 -0
  48. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/x-dm.js +101 -0
  49. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/x-dm.js.map +1 -0
  50. package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-schema.d.ts.map +1 -1
  51. package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-schema.js +7 -4
  52. package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-schema.js.map +1 -1
  53. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.d.ts +59 -1
  54. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.d.ts.map +1 -1
  55. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js +322 -0
  56. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js.map +1 -1
  57. package/payload/platform/plugins/memory/references/schema-base.md +4 -4
  58. package/payload/platform/plugins/memory/skills/conversation-archive/SKILL.md +2 -1
  59. package/payload/platform/plugins/memory/skills/document-ingest/SKILL.md +1 -0
  60. package/payload/platform/plugins/notion-import/skills/notion-import/SKILL.md +13 -9
  61. package/payload/platform/plugins/notion-import/skills/notion-import/references/databases.md +11 -9
  62. package/payload/platform/plugins/notion-import/skills/notion-import/references/page-tree.md +12 -12
  63. package/payload/platform/plugins/scheduling/PLUGIN.md +18 -0
  64. package/payload/platform/plugins/scheduling/mcp/dist/index.js +93 -0
  65. package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -1
  66. package/payload/platform/plugins/scheduling/mcp/dist/lib/ics-graph-ingest.d.ts +80 -0
  67. package/payload/platform/plugins/scheduling/mcp/dist/lib/ics-graph-ingest.d.ts.map +1 -0
  68. package/payload/platform/plugins/scheduling/mcp/dist/lib/ics-graph-ingest.js +395 -0
  69. package/payload/platform/plugins/scheduling/mcp/dist/lib/ics-graph-ingest.js.map +1 -0
  70. package/payload/platform/plugins/scheduling/mcp/dist/lib/ics.d.ts +16 -0
  71. package/payload/platform/plugins/scheduling/mcp/dist/lib/ics.d.ts.map +1 -1
  72. package/payload/platform/plugins/scheduling/mcp/dist/lib/ics.js +111 -2
  73. package/payload/platform/plugins/scheduling/mcp/dist/lib/ics.js.map +1 -1
  74. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-archive-ics.d.ts +20 -0
  75. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-archive-ics.d.ts.map +1 -0
  76. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-archive-ics.js +63 -0
  77. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-archive-ics.js.map +1 -0
  78. package/payload/platform/plugins/x-import/.claude-plugin/plugin.json +8 -0
  79. package/payload/platform/plugins/x-import/PLUGIN.md +33 -0
  80. package/payload/platform/plugins/x-import/references/archive-shape.md +124 -0
  81. package/payload/platform/plugins/x-import/references/transcript-shape.md +121 -0
  82. package/payload/platform/plugins/x-import/skills/x-import/SKILL.md +123 -0
  83. package/payload/platform/templates/specialists/agents/database-operator.md +6 -2
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Graph-ingest path for ICS calendar archives.
3
+ *
4
+ * .ics file ──▶ parseICS ──▶ (dry-run preview │ commit writes)
5
+ *
6
+ * Writes one `:Meeting` per VEVENT, anchors it to the importing `:AdminUser`
7
+ * via `[:IMPORTED]`, resolves attendees to `:Person` by lowercased email, and
8
+ * MERGEs `[:ATTENDED]` edges with role + partstat.
9
+ *
10
+ * Cross-account collision rule: `Person.email` is globally unique in the
11
+ * schema. When an attendee email already exists under a different accountId,
12
+ * the import skips the attendance edge (no edge written, the foreign Person
13
+ * is untouched) and increments `skippedCrossAccountPerson`. The Meeting still
14
+ * lands, anchored to the importer's AdminUser. This preserves absolute brand
15
+ * / account isolation while keeping the import as a whole non-fatal.
16
+ */
17
+ import { Session } from "neo4j-driver";
18
+ import { type ParsedAttendee } from "./ics.js";
19
+ export interface IcsIngestParams {
20
+ /** The .ics file content (already read by the tool wrapper). */
21
+ fileContent: string;
22
+ /** Path-cleansed filename used for diagnostics + `:Meeting.sourceFilename`. */
23
+ filename: string;
24
+ mode: "dry-run" | "commit";
25
+ accountId: string;
26
+ /** The importing AdminUser. Required — we anchor every Meeting to them. */
27
+ userId: string;
28
+ sessionId?: string;
29
+ session: Session;
30
+ }
31
+ export interface AttendeePreviewEntry {
32
+ address: string;
33
+ displayName?: string;
34
+ status: "existing-same-account" | "existing-cross-account" | "will-create" | "non-mailto";
35
+ /** Set when status is existing-*. */
36
+ existingDisplayName?: string;
37
+ }
38
+ export interface IcsIngestDryRunResult {
39
+ mode: "dry-run";
40
+ filename: string;
41
+ importId: string;
42
+ events: number;
43
+ validMeetings: number;
44
+ skippedNoUid: number;
45
+ skippedNoStartTime: number;
46
+ /** Aggregate attendee preview. */
47
+ preview: {
48
+ attendees: number;
49
+ organizers: number;
50
+ nonMailtoAttendees: number;
51
+ existingSameAccount: number;
52
+ existingCrossAccount: number;
53
+ willCreate: number;
54
+ };
55
+ /** Up to 20 representative attendees, classified — surfaces what a commit would do. */
56
+ sampleAttendees: AttendeePreviewEntry[];
57
+ parseErrors: string[];
58
+ ms: number;
59
+ }
60
+ export interface IcsIngestCommitResult {
61
+ mode: "commit";
62
+ filename: string;
63
+ importId: string;
64
+ events: number;
65
+ meetingsCreated: number;
66
+ meetingsUpdated: number;
67
+ peopleCreated: number;
68
+ existingPersonsLinked: number;
69
+ attendees: number;
70
+ organizers: number;
71
+ skippedNoUid: number;
72
+ skippedNoStartTime: number;
73
+ skippedCrossAccountPerson: number;
74
+ parseErrors: string[];
75
+ ms: number;
76
+ }
77
+ export type IcsIngestResult = IcsIngestDryRunResult | IcsIngestCommitResult;
78
+ export declare function ingestIcs(params: IcsIngestParams): Promise<IcsIngestResult>;
79
+ export type { ParsedAttendee };
80
+ //# sourceMappingURL=ics-graph-ingest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ics-graph-ingest.d.ts","sourceRoot":"","sources":["../../src/lib/ics-graph-ingest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAA8B,KAAK,cAAc,EAAE,MAAM,UAAU,CAAC;AAM3E,MAAM,WAAW,eAAe;IAC9B,gEAAgE;IAChE,WAAW,EAAE,MAAM,CAAC;IACpB,+EAA+E;IAC/E,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,2EAA2E;IAC3E,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EACF,uBAAuB,GACvB,wBAAwB,GACxB,aAAa,GACb,YAAY,CAAC;IACjB,qCAAqC;IACrC,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,SAAS,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,kCAAkC;IAClC,OAAO,EAAE;QACP,SAAS,EAAE,MAAM,CAAC;QAClB,UAAU,EAAE,MAAM,CAAC;QACnB,kBAAkB,EAAE,MAAM,CAAC;QAC3B,mBAAmB,EAAE,MAAM,CAAC;QAC5B,oBAAoB,EAAE,MAAM,CAAC;QAC7B,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,uFAAuF;IACvF,eAAe,EAAE,oBAAoB,EAAE,CAAC;IACxC,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,yBAAyB,EAAE,MAAM,CAAC;IAClC,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,MAAM,eAAe,GAAG,qBAAqB,GAAG,qBAAqB,CAAC;AAM5E,wBAAsB,SAAS,CAC7B,MAAM,EAAE,eAAe,GACtB,OAAO,CAAC,eAAe,CAAC,CA8D1B;AAkYD,YAAY,EAAE,cAAc,EAAE,CAAC"}
@@ -0,0 +1,395 @@
1
+ /**
2
+ * Graph-ingest path for ICS calendar archives.
3
+ *
4
+ * .ics file ──▶ parseICS ──▶ (dry-run preview │ commit writes)
5
+ *
6
+ * Writes one `:Meeting` per VEVENT, anchors it to the importing `:AdminUser`
7
+ * via `[:IMPORTED]`, resolves attendees to `:Person` by lowercased email, and
8
+ * MERGEs `[:ATTENDED]` edges with role + partstat.
9
+ *
10
+ * Cross-account collision rule: `Person.email` is globally unique in the
11
+ * schema. When an attendee email already exists under a different accountId,
12
+ * the import skips the attendance edge (no edge written, the foreign Person
13
+ * is untouched) and increments `skippedCrossAccountPerson`. The Meeting still
14
+ * lands, anchored to the importer's AdminUser. This preserves absolute brand
15
+ * / account isolation while keeping the import as a whole non-fatal.
16
+ */
17
+ import { randomUUID } from "node:crypto";
18
+ import { parseICS } from "./ics.js";
19
+ // ---------------------------------------------------------------------------
20
+ // Entry point
21
+ // ---------------------------------------------------------------------------
22
+ export async function ingestIcs(params) {
23
+ const start = Date.now();
24
+ const importId = randomUUID();
25
+ const { fileContent, filename, mode, accountId, userId, sessionId, session } = params;
26
+ const parsed = parseICS(fileContent);
27
+ // Partition events into valid / skipped buckets. parseICS already rejects
28
+ // VEVENTs without DTSTART (they land in parseErrors); the parser stops short
29
+ // of inferring skip-reason buckets, so we classify here too. UID is required
30
+ // for the (accountId, uid) composite key.
31
+ const valid = [];
32
+ let skippedNoUid = 0;
33
+ let skippedNoStartTime = 0;
34
+ for (const ev of parsed.events) {
35
+ if (!ev.dtstart) {
36
+ skippedNoStartTime++;
37
+ continue;
38
+ }
39
+ if (!ev.uid) {
40
+ skippedNoUid++;
41
+ continue;
42
+ }
43
+ valid.push(ev);
44
+ }
45
+ // Events the parser already rejected for missing DTSTART surface via
46
+ // parseErrors. Count those too so the operator-visible signal matches the
47
+ // file's actual content.
48
+ for (const err of parsed.parseErrors) {
49
+ if (err.includes("missing required DTSTART"))
50
+ skippedNoStartTime++;
51
+ }
52
+ if (mode === "dry-run") {
53
+ return dryRunPreview({
54
+ session,
55
+ accountId,
56
+ valid,
57
+ filename,
58
+ importId,
59
+ skippedNoUid,
60
+ skippedNoStartTime,
61
+ parseErrors: parsed.parseErrors,
62
+ eventsParsed: parsed.events.length,
63
+ start,
64
+ });
65
+ }
66
+ return commit({
67
+ session,
68
+ accountId,
69
+ userId,
70
+ sessionId,
71
+ valid,
72
+ filename,
73
+ importId,
74
+ skippedNoUid,
75
+ skippedNoStartTime,
76
+ parseErrors: parsed.parseErrors,
77
+ eventsParsed: parsed.events.length,
78
+ start,
79
+ });
80
+ }
81
+ async function dryRunPreview(ctx) {
82
+ // Flatten attendees across valid events. Track raw counts so the preview
83
+ // numbers match what the commit phase would emit.
84
+ let attendees = 0;
85
+ let organizers = 0;
86
+ let nonMailto = 0;
87
+ const addressOrder = []; // dedup-preserving insertion order
88
+ const addressSeen = new Set();
89
+ const addressDisplayName = new Map();
90
+ for (const ev of ctx.valid) {
91
+ for (const a of ev.attendees) {
92
+ if (a.role === "organizer")
93
+ organizers++;
94
+ else
95
+ attendees++;
96
+ if (!a.address) {
97
+ nonMailto++;
98
+ continue;
99
+ }
100
+ if (!addressSeen.has(a.address)) {
101
+ addressSeen.add(a.address);
102
+ addressOrder.push(a.address);
103
+ }
104
+ if (a.displayName && !addressDisplayName.has(a.address)) {
105
+ addressDisplayName.set(a.address, a.displayName);
106
+ }
107
+ }
108
+ }
109
+ // One round-trip to classify every unique address against the live graph.
110
+ const existing = new Map();
111
+ if (addressOrder.length > 0) {
112
+ const result = await ctx.session.run(`
113
+ UNWIND $emails AS email
114
+ MATCH (p:Person {email: email})
115
+ RETURN p.email AS email, p.accountId AS accountId, p.displayName AS displayName
116
+ `, { emails: addressOrder });
117
+ for (const rec of result.records) {
118
+ existing.set(rec.get("email"), {
119
+ accountId: rec.get("accountId") ?? null,
120
+ displayName: rec.get("displayName") ?? null,
121
+ });
122
+ }
123
+ }
124
+ let existingSameAccount = 0;
125
+ let existingCrossAccount = 0;
126
+ let willCreate = 0;
127
+ const sample = [];
128
+ const SAMPLE_CAP = 20;
129
+ for (const address of addressOrder) {
130
+ const hit = existing.get(address);
131
+ let status;
132
+ let existingDisplayName;
133
+ if (!hit) {
134
+ willCreate++;
135
+ status = "will-create";
136
+ }
137
+ else if (hit.accountId === ctx.accountId) {
138
+ existingSameAccount++;
139
+ status = "existing-same-account";
140
+ if (hit.displayName)
141
+ existingDisplayName = hit.displayName;
142
+ }
143
+ else {
144
+ existingCrossAccount++;
145
+ status = "existing-cross-account";
146
+ if (hit.displayName)
147
+ existingDisplayName = hit.displayName;
148
+ }
149
+ if (sample.length < SAMPLE_CAP) {
150
+ const entry = { address, status };
151
+ const dn = addressDisplayName.get(address);
152
+ if (dn)
153
+ entry.displayName = dn;
154
+ if (existingDisplayName)
155
+ entry.existingDisplayName = existingDisplayName;
156
+ sample.push(entry);
157
+ }
158
+ }
159
+ // Non-mailto attendees are interesting enough to sample at least one.
160
+ if (nonMailto > 0 && sample.length < SAMPLE_CAP) {
161
+ for (const ev of ctx.valid) {
162
+ for (const a of ev.attendees) {
163
+ if (a.address)
164
+ continue;
165
+ sample.push({
166
+ address: a.rawValue,
167
+ status: "non-mailto",
168
+ ...(a.displayName ? { displayName: a.displayName } : {}),
169
+ });
170
+ if (sample.length >= SAMPLE_CAP)
171
+ break;
172
+ }
173
+ if (sample.length >= SAMPLE_CAP)
174
+ break;
175
+ }
176
+ }
177
+ const ms = Date.now() - ctx.start;
178
+ process.stderr.write(`[ics-ingest] importId=${ctx.importId} filename=${ctx.filename} mode=dry-run ` +
179
+ `events=${ctx.eventsParsed} meetings=${ctx.valid.length} ` +
180
+ `attendees=${attendees} organizers=${organizers} nonMailto=${nonMailto} ` +
181
+ `existingSame=${existingSameAccount} existingCross=${existingCrossAccount} ` +
182
+ `willCreate=${willCreate} skippedNoUid=${ctx.skippedNoUid} ` +
183
+ `skippedNoStartTime=${ctx.skippedNoStartTime} ms=${ms}\n`);
184
+ return {
185
+ mode: "dry-run",
186
+ filename: ctx.filename,
187
+ importId: ctx.importId,
188
+ events: ctx.eventsParsed,
189
+ validMeetings: ctx.valid.length,
190
+ skippedNoUid: ctx.skippedNoUid,
191
+ skippedNoStartTime: ctx.skippedNoStartTime,
192
+ preview: {
193
+ attendees,
194
+ organizers,
195
+ nonMailtoAttendees: nonMailto,
196
+ existingSameAccount,
197
+ existingCrossAccount,
198
+ willCreate,
199
+ },
200
+ sampleAttendees: sample,
201
+ parseErrors: ctx.parseErrors,
202
+ ms,
203
+ };
204
+ }
205
+ async function commit(ctx) {
206
+ // Resolve the importing AdminUser once. We refuse to start the commit if
207
+ // the userId doesn't resolve — anchoring every Meeting to a real AdminUser
208
+ // is what keeps :Meeting from being an orphan.
209
+ const adminCheck = await ctx.session.run(`MATCH (a:AdminUser {userId: $userId}) RETURN elementId(a) AS id LIMIT 1`, { userId: ctx.userId });
210
+ if (adminCheck.records.length === 0) {
211
+ throw new Error(`ics-ingest: no :AdminUser found for userId=${ctx.userId}. ` +
212
+ `The importer must exist in the graph before ingesting an .ics archive.`);
213
+ }
214
+ let meetingsCreated = 0;
215
+ let meetingsUpdated = 0;
216
+ let peopleCreated = 0;
217
+ let existingPersonsLinked = 0;
218
+ let skippedCrossAccountPerson = 0;
219
+ let attendees = 0;
220
+ let organizers = 0;
221
+ // One transaction per meeting keeps the blast radius small — if attendee
222
+ // resolution fails on event #4000, events 1..3999 are already durably
223
+ // committed. Calendar archives are long flat lists, not interlinked.
224
+ for (const ev of ctx.valid) {
225
+ const meetingProps = {
226
+ uid: ev.uid,
227
+ title: ev.summary,
228
+ startsAt: ev.dtstart,
229
+ endsAt: ev.dtend ?? null,
230
+ location: ev.location ?? null,
231
+ description: ev.description ?? null,
232
+ isAllDay: ev.isAllDay,
233
+ sourceFilename: ctx.filename,
234
+ accountId: ctx.accountId,
235
+ sessionId: ctx.sessionId ?? null,
236
+ };
237
+ const meetingResult = await ctx.session.executeWrite(async (tx) => {
238
+ const r = await tx.run(`
239
+ MATCH (a:AdminUser {userId: $userId})
240
+ MERGE (m:Meeting {accountId: $accountId, uid: $props.uid})
241
+ ON CREATE SET
242
+ m.title = $props.title,
243
+ m.startsAt = datetime($props.startsAt),
244
+ m.endsAt = CASE WHEN $props.endsAt IS NULL THEN null ELSE datetime($props.endsAt) END,
245
+ m.location = $props.location,
246
+ m.description = $props.description,
247
+ m.isAllDay = $props.isAllDay,
248
+ m.source = 'ics',
249
+ m.sourceFilename = $props.sourceFilename,
250
+ m.scope = 'admin',
251
+ m.createdAt = datetime(),
252
+ m.updatedAt = datetime(),
253
+ m.createdByAgent = 'ics-ingest',
254
+ m.createdBySource = 'ics-ingest',
255
+ m.createdBySession = $sessionId,
256
+ m._created = true
257
+ ON MATCH SET
258
+ m.title = $props.title,
259
+ m.startsAt = datetime($props.startsAt),
260
+ m.endsAt = CASE WHEN $props.endsAt IS NULL THEN null ELSE datetime($props.endsAt) END,
261
+ m.location = $props.location,
262
+ m.description = $props.description,
263
+ m.isAllDay = $props.isAllDay,
264
+ m.sourceFilename = $props.sourceFilename,
265
+ m.updatedAt = datetime(),
266
+ m._created = false
267
+ WITH a, m, m._created AS created
268
+ REMOVE m._created
269
+ MERGE (a)-[imp:IMPORTED]->(m)
270
+ ON CREATE SET imp.createdAt = datetime(), imp.source = 'ics-ingest'
271
+ RETURN created, elementId(m) AS meetingId
272
+ `, {
273
+ userId: ctx.userId,
274
+ accountId: ctx.accountId,
275
+ props: meetingProps,
276
+ sessionId: ctx.sessionId ?? null,
277
+ });
278
+ const created = r.records[0].get("created");
279
+ const meetingId = r.records[0].get("meetingId");
280
+ return { created, meetingId };
281
+ });
282
+ if (meetingResult.created)
283
+ meetingsCreated++;
284
+ else
285
+ meetingsUpdated++;
286
+ // Attendee + organizer resolution. We separate organizers from attendees
287
+ // only for counting; the edge written is :ATTENDED in both cases, with the
288
+ // role property carrying the distinction.
289
+ for (const att of ev.attendees) {
290
+ if (att.role === "organizer")
291
+ organizers++;
292
+ else
293
+ attendees++;
294
+ if (!att.address)
295
+ continue; // non-mailto: counted but no graph effect
296
+ const personResult = await ctx.session.executeWrite(async (tx) => {
297
+ // Pre-flight existence check so we never silently create an :ATTENDED
298
+ // edge from a foreign-accountId Person.
299
+ const lookup = await tx.run(`MATCH (p:Person {email: $email}) RETURN p.accountId AS accountId LIMIT 1`, { email: att.address });
300
+ const existingAccountId = lookup.records[0]?.get("accountId");
301
+ if (existingAccountId && existingAccountId !== ctx.accountId) {
302
+ return { outcome: "cross-account-skip" };
303
+ }
304
+ if (existingAccountId === ctx.accountId) {
305
+ // Same-account Person: link without overwriting their identity.
306
+ await tx.run(`
307
+ MATCH (p:Person {email: $email})
308
+ MATCH (m:Meeting) WHERE elementId(m) = $meetingId
309
+ MERGE (p)-[r:ATTENDED]->(m)
310
+ ON CREATE SET r.role = $role, r.partstat = $partstat,
311
+ r.createdAt = datetime(),
312
+ r.createdBySource = 'ics-ingest'
313
+ ON MATCH SET r.role = $role, r.partstat = $partstat
314
+ FOREACH (_ IN CASE WHEN p.displayName IS NULL AND $displayName IS NOT NULL THEN [1] ELSE [] END |
315
+ SET p.displayName = $displayName
316
+ )
317
+ `, {
318
+ email: att.address,
319
+ meetingId: meetingResult.meetingId,
320
+ role: att.role,
321
+ partstat: att.partstat ?? null,
322
+ displayName: att.displayName ?? null,
323
+ });
324
+ return { outcome: "linked-existing" };
325
+ }
326
+ // Brand new Person stub under the importer's account.
327
+ await tx.run(`
328
+ MATCH (m:Meeting) WHERE elementId(m) = $meetingId
329
+ CREATE (p:Person {
330
+ email: $email,
331
+ accountId: $accountId,
332
+ scope: 'admin',
333
+ source: 'ics-ingest',
334
+ displayName: $displayName,
335
+ createdAt: datetime(),
336
+ createdByAgent: 'ics-ingest',
337
+ createdBySource: 'ics-ingest',
338
+ createdBySession: $sessionId
339
+ })
340
+ CREATE (p)-[:ATTENDED {
341
+ role: $role,
342
+ partstat: $partstat,
343
+ createdAt: datetime(),
344
+ createdBySource: 'ics-ingest'
345
+ }]->(m)
346
+ `, {
347
+ email: att.address,
348
+ accountId: ctx.accountId,
349
+ displayName: att.displayName ?? null,
350
+ sessionId: ctx.sessionId ?? null,
351
+ meetingId: meetingResult.meetingId,
352
+ role: att.role,
353
+ partstat: att.partstat ?? null,
354
+ });
355
+ return { outcome: "person-created" };
356
+ });
357
+ if (personResult.outcome === "cross-account-skip") {
358
+ skippedCrossAccountPerson++;
359
+ }
360
+ else if (personResult.outcome === "linked-existing") {
361
+ existingPersonsLinked++;
362
+ }
363
+ else {
364
+ peopleCreated++;
365
+ }
366
+ }
367
+ }
368
+ const ms = Date.now() - ctx.start;
369
+ process.stderr.write(`[ics-ingest] importId=${ctx.importId} filename=${ctx.filename} mode=commit ` +
370
+ `events=${ctx.eventsParsed} meetings=${ctx.valid.length} ` +
371
+ `meetingsCreated=${meetingsCreated} meetingsUpdated=${meetingsUpdated} ` +
372
+ `peopleCreated=${peopleCreated} existingPersonsLinked=${existingPersonsLinked} ` +
373
+ `attendees=${attendees} organizers=${organizers} ` +
374
+ `skippedCrossAccountPerson=${skippedCrossAccountPerson} ` +
375
+ `skippedNoUid=${ctx.skippedNoUid} skippedNoStartTime=${ctx.skippedNoStartTime} ` +
376
+ `ms=${ms}\n`);
377
+ return {
378
+ mode: "commit",
379
+ filename: ctx.filename,
380
+ importId: ctx.importId,
381
+ events: ctx.eventsParsed,
382
+ meetingsCreated,
383
+ meetingsUpdated,
384
+ peopleCreated,
385
+ existingPersonsLinked,
386
+ attendees,
387
+ organizers,
388
+ skippedNoUid: ctx.skippedNoUid,
389
+ skippedNoStartTime: ctx.skippedNoStartTime,
390
+ skippedCrossAccountPerson,
391
+ parseErrors: ctx.parseErrors,
392
+ ms,
393
+ };
394
+ }
395
+ //# sourceMappingURL=ics-graph-ingest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ics-graph-ingest.js","sourceRoot":"","sources":["../../src/lib/ics-graph-ingest.ts"],"names":[],"mappings":"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"}
@@ -20,6 +20,18 @@ export interface ICSEvent {
20
20
  recurrence?: string;
21
21
  timezone?: string;
22
22
  }
23
+ export type AttendeeRole = "organizer" | "chair" | "req-participant" | "opt-participant" | "non-participant";
24
+ export interface ParsedAttendee {
25
+ /** Lowercased email local+domain from `mailto:` URI; null for non-mailto values (rooms, resources, groups). */
26
+ address: string | null;
27
+ /** CN parameter, unescaped. */
28
+ displayName?: string;
29
+ role: AttendeeRole;
30
+ /** PARTSTAT parameter, lowercased (e.g. "accepted", "declined", "tentative", "needs-action"). */
31
+ partstat?: string;
32
+ /** Raw URI value, kept verbatim for visibility on non-mailto entries. */
33
+ rawValue: string;
34
+ }
23
35
  export interface ParsedEvent {
24
36
  summary: string;
25
37
  dtstart: string;
@@ -28,6 +40,10 @@ export interface ParsedEvent {
28
40
  description?: string;
29
41
  uid?: string;
30
42
  recurrence?: string;
43
+ /** True when DTSTART carries `VALUE=DATE` (RFC 5545 §3.3.4 date-only). */
44
+ isAllDay: boolean;
45
+ /** Every ATTENDEE plus the ORGANIZER if present. Empty array if none. */
46
+ attendees: ParsedAttendee[];
31
47
  }
32
48
  export interface ParseResult {
33
49
  events: ParsedEvent[];
@@ -1 +1 @@
1
- {"version":3,"file":"ics.d.ts","sourceRoot":"","sources":["../../src/lib/ics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AASH,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AA6GD,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAkCrD;AAmCD,MAAM,WAAW,eAAe;IAC9B,8EAA8E;IAC9E,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,QAAQ,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,MAAM,CA6BjF;AAiFD,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,CA6GrD"}
1
+ {"version":3,"file":"ics.d.ts","sourceRoot":"","sources":["../../src/lib/ics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AASH,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,YAAY,GACpB,WAAW,GACX,OAAO,GACP,iBAAiB,GACjB,iBAAiB,GACjB,iBAAiB,CAAC;AAEtB,MAAM,WAAW,cAAc;IAC7B,+GAA+G;IAC/G,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,+BAA+B;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,YAAY,CAAC;IACnB,iGAAiG;IACjG,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0EAA0E;IAC1E,QAAQ,EAAE,OAAO,CAAC;IAClB,yEAAyE;IACzE,SAAS,EAAE,cAAc,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AA6GD,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAkCrD;AAmCD,MAAM,WAAW,eAAe;IAC9B,8EAA8E;IAC9E,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,QAAQ,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,MAAM,CA6BjF;AAiFD,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,CAkJrD"}