@pellux/goodvibes-tui 0.25.0 → 0.26.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.
- package/CHANGELOG.md +5 -3
- package/README.md +1 -1
- package/docs/foundation-artifacts/operator-contract.json +2419 -1040
- package/package.json +2 -2
- package/src/daemon/{calendar → handlers/calendar}/caldav-client.ts +28 -24
- package/src/daemon/handlers/calendar/index.ts +316 -0
- package/src/daemon/handlers/context.ts +29 -0
- package/src/daemon/handlers/contracts.ts +77 -0
- package/src/daemon/{channels → handlers}/drafts/draft-store.ts +114 -50
- package/src/daemon/handlers/drafts/index.ts +17 -0
- package/src/daemon/handlers/drafts/register.ts +331 -0
- package/src/daemon/handlers/email/config.ts +164 -0
- package/src/daemon/handlers/email/index.ts +43 -0
- package/src/daemon/handlers/email/read-handlers.ts +80 -0
- package/src/daemon/handlers/email/runtime.ts +140 -0
- package/src/daemon/handlers/email/validation.ts +147 -0
- package/src/daemon/handlers/email/write-handlers.ts +133 -0
- package/src/daemon/handlers/errors.ts +18 -0
- package/src/daemon/{channels → handlers}/inbox/cursor-store.ts +58 -66
- package/src/daemon/handlers/inbox/index.ts +210 -0
- package/src/daemon/{channels → handlers}/inbox/mapping.ts +8 -6
- package/src/daemon/{channels → handlers}/inbox/poller.ts +4 -4
- package/src/daemon/{channels → handlers}/inbox/provider-adapter.ts +14 -10
- package/src/daemon/{channels → handlers}/inbox/providers/discord.ts +8 -10
- package/src/daemon/{channels → handlers}/inbox/providers/imap-client.ts +1 -1
- package/src/daemon/{channels → handlers}/inbox/providers/route-util.ts +2 -2
- package/src/daemon/{channels → handlers}/inbox/providers/slack.ts +9 -11
- package/src/daemon/handlers/index.ts +107 -0
- package/src/daemon/handlers/register.ts +161 -0
- package/src/daemon/{remote → handlers/remote}/backends/cloud-terminal.ts +8 -3
- package/src/daemon/{remote → handlers/remote}/backends/docker.ts +2 -3
- package/src/daemon/handlers/remote/backends/index.ts +40 -0
- package/src/daemon/{remote → handlers/remote}/backends/process-runner.ts +16 -40
- package/src/daemon/{remote → handlers/remote}/backends/ssh.ts +8 -3
- package/src/daemon/{remote → handlers/remote}/backends/types.ts +29 -3
- package/src/daemon/{remote → handlers/remote}/dispatcher.ts +27 -6
- package/src/daemon/handlers/remote/index.ts +119 -0
- package/src/daemon/{remote → handlers/remote}/peer-registry.ts +47 -11
- package/src/daemon/handlers/remote/service.ts +191 -0
- package/src/daemon/{channels → handlers}/routing/inbox-bridge.ts +33 -20
- package/src/daemon/handlers/routing/index.ts +261 -0
- package/src/daemon/{channels → handlers}/routing/route-store.ts +57 -16
- package/src/daemon/{channels → handlers}/routing/routing-resolver.ts +1 -1
- package/src/daemon/{operator → handlers}/sqlite-store.ts +5 -5
- package/src/daemon/handlers/triage/index.ts +57 -0
- package/src/daemon/handlers/triage/integration.ts +212 -0
- package/src/daemon/{triage → handlers/triage}/pipeline.ts +58 -70
- package/src/daemon/{triage → handlers/triage}/scorer.ts +21 -21
- package/src/daemon/handlers/triage/tagger/discord.ts +186 -0
- package/src/daemon/handlers/triage/tagger/imap.ts +383 -0
- package/src/daemon/handlers/triage/tagger/index.ts +184 -0
- package/src/daemon/handlers/triage/tagger/shared.ts +70 -0
- package/src/daemon/handlers/triage/tagger/slack.ts +69 -0
- package/src/daemon/handlers/triage/types.ts +50 -0
- package/src/runtime/services.ts +48 -35
- package/src/version.ts +1 -1
- package/src/daemon/calendar/index.ts +0 -52
- package/src/daemon/calendar/register.ts +0 -527
- package/src/daemon/channels/drafts/index.ts +0 -22
- package/src/daemon/channels/drafts/register.ts +0 -449
- package/src/daemon/channels/inbox/index.ts +0 -58
- package/src/daemon/channels/inbox/register.ts +0 -247
- package/src/daemon/channels/routing/index.ts +0 -39
- package/src/daemon/channels/routing/register.ts +0 -296
- package/src/daemon/email/index.ts +0 -68
- package/src/daemon/email/register.ts +0 -715
- package/src/daemon/operator/index.ts +0 -43
- package/src/daemon/operator/register-helper.ts +0 -150
- package/src/daemon/operator/surfaces.ts +0 -137
- package/src/daemon/operator/types.ts +0 -207
- package/src/daemon/remote/backends/index.ts +0 -34
- package/src/daemon/remote/index.ts +0 -74
- package/src/daemon/remote/register.ts +0 -411
- package/src/daemon/triage/index.ts +0 -59
- package/src/daemon/triage/integration.ts +0 -179
- package/src/daemon/triage/register.ts +0 -231
- package/src/daemon/triage/tagger.ts +0 -777
- /package/src/daemon/{calendar → handlers/calendar}/ics.ts +0 -0
- /package/src/daemon/{operator/credential-store.ts → handlers/credentials.ts} +0 -0
- /package/src/daemon/{email → handlers/email}/imap-connector.ts +0 -0
- /package/src/daemon/{email → handlers/email}/imap-parsing.ts +0 -0
- /package/src/daemon/{email → handlers/email}/smtp-connector.ts +0 -0
- /package/src/daemon/{channels → handlers}/inbox/providers/email.ts +0 -0
- /package/src/daemon/{remote → handlers/remote}/backends/local-process.ts +0 -0
|
@@ -1,527 +0,0 @@
|
|
|
1
|
-
// Operator-method registration for the CalDAV calendar surface.
|
|
2
|
-
//
|
|
3
|
-
// Published method IDs (exactly):
|
|
4
|
-
// calendar.events.list (read-only-network, no confirm)
|
|
5
|
-
// calendar.events.get (read-only-network, no confirm)
|
|
6
|
-
// calendar.events.create (confirmed-effect, confirm:true + explicitUserRequest)
|
|
7
|
-
// calendar.ics.import (confirmed-effect, confirm:true)
|
|
8
|
-
// calendar.ics.export (read-only, no confirm)
|
|
9
|
-
//
|
|
10
|
-
// Exactly these five IDs are published — matching the SDK handoff contract
|
|
11
|
-
// ("the daemon must implement exactly these IDs"). Capability advertisement is
|
|
12
|
-
// satisfied by each descriptor's catalog metadata rather than by an extra
|
|
13
|
-
// calendar.* operator method.
|
|
14
|
-
//
|
|
15
|
-
// SECURITY: CalDAV credentials and authenticated URLs NEVER appear in any
|
|
16
|
-
// response. `calendarId` is a logical id. Attendees are surfaced as display
|
|
17
|
-
// names only (no raw addresses). Organizer is surfaced as a SHA-256 digest.
|
|
18
|
-
|
|
19
|
-
import {
|
|
20
|
-
declareOperatorMethods,
|
|
21
|
-
OperatorError,
|
|
22
|
-
sha256First,
|
|
23
|
-
type CalendarEventSummary,
|
|
24
|
-
type OperatorContext,
|
|
25
|
-
type OperatorHandler,
|
|
26
|
-
type OperatorMethodDescriptor,
|
|
27
|
-
type Unregister,
|
|
28
|
-
} from '../operator/index.ts';
|
|
29
|
-
import {
|
|
30
|
-
createCalDavClient,
|
|
31
|
-
resolveCalDavConfig,
|
|
32
|
-
type CalDavClient,
|
|
33
|
-
type CalDavEvent,
|
|
34
|
-
} from './caldav-client.ts';
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* The exact set of method IDs this surface publishes. Exposed so integration
|
|
38
|
-
* and tests have a single source of truth for the calendar capability set.
|
|
39
|
-
*/
|
|
40
|
-
export const CALENDAR_METHOD_IDS = [
|
|
41
|
-
'calendar.events.list',
|
|
42
|
-
'calendar.events.get',
|
|
43
|
-
'calendar.events.create',
|
|
44
|
-
'calendar.ics.import',
|
|
45
|
-
'calendar.ics.export',
|
|
46
|
-
] as const;
|
|
47
|
-
|
|
48
|
-
const CATEGORY = 'calendar';
|
|
49
|
-
const TRANSPORT: OperatorMethodDescriptor['transport'] = ['ws', 'internal'];
|
|
50
|
-
|
|
51
|
-
// ---------------------------------------------------------------------------
|
|
52
|
-
// Client factory injection (real client by default; tests inject a stub).
|
|
53
|
-
// ---------------------------------------------------------------------------
|
|
54
|
-
|
|
55
|
-
export type CalDavClientFactory = (ctx: OperatorContext) => Promise<CalDavClient>;
|
|
56
|
-
|
|
57
|
-
const realClientFactory: CalDavClientFactory = async (ctx) => {
|
|
58
|
-
const config = await resolveCalDavConfig(ctx);
|
|
59
|
-
return createCalDavClient({ config });
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
export interface RegisterCalendarOptions {
|
|
63
|
-
/** Override the CalDAV client factory (for tests). */
|
|
64
|
-
clientFactory?: CalDavClientFactory;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// ---------------------------------------------------------------------------
|
|
68
|
-
// Input validation helpers
|
|
69
|
-
// ---------------------------------------------------------------------------
|
|
70
|
-
|
|
71
|
-
function asRecord(body: unknown): Record<string, unknown> {
|
|
72
|
-
if (body === null || typeof body !== 'object' || Array.isArray(body)) {
|
|
73
|
-
throw new OperatorError('Request body must be an object.', 'CALENDAR_BAD_INPUT', 400);
|
|
74
|
-
}
|
|
75
|
-
return body as Record<string, unknown>;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function optionalString(record: Record<string, unknown>, key: string): string | undefined {
|
|
79
|
-
const value = record[key];
|
|
80
|
-
if (value === undefined || value === null) return undefined;
|
|
81
|
-
if (typeof value !== 'string') {
|
|
82
|
-
throw new OperatorError(`Field '${key}' must be a string.`, 'CALENDAR_BAD_INPUT', 400);
|
|
83
|
-
}
|
|
84
|
-
const trimmed = value.trim();
|
|
85
|
-
return trimmed.length > 0 ? trimmed : undefined;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function requiredString(record: Record<string, unknown>, key: string): string {
|
|
89
|
-
const value = optionalString(record, key);
|
|
90
|
-
if (value === undefined) {
|
|
91
|
-
throw new OperatorError(`Field '${key}' is required.`, 'CALENDAR_BAD_INPUT', 400);
|
|
92
|
-
}
|
|
93
|
-
return value;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function optionalNumber(record: Record<string, unknown>, key: string): number | undefined {
|
|
97
|
-
const value = record[key];
|
|
98
|
-
if (value === undefined || value === null) return undefined;
|
|
99
|
-
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
100
|
-
throw new OperatorError(`Field '${key}' must be a number.`, 'CALENDAR_BAD_INPUT', 400);
|
|
101
|
-
}
|
|
102
|
-
return value;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function optionalStringArray(record: Record<string, unknown>, key: string): string[] | undefined {
|
|
106
|
-
const value = record[key];
|
|
107
|
-
if (value === undefined || value === null) return undefined;
|
|
108
|
-
if (!Array.isArray(value) || value.some((item) => typeof item !== 'string')) {
|
|
109
|
-
throw new OperatorError(`Field '${key}' must be an array of strings.`, 'CALENDAR_BAD_INPUT', 400);
|
|
110
|
-
}
|
|
111
|
-
return (value as string[]).map((item) => item.trim()).filter((item) => item.length > 0);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function validateIsoDate(value: string, field: string): string {
|
|
115
|
-
if (Number.isNaN(new Date(value).getTime())) {
|
|
116
|
-
throw new OperatorError(`Field '${field}' must be a valid ISO-8601 date.`, 'CALENDAR_BAD_INPUT', 400);
|
|
117
|
-
}
|
|
118
|
-
return value;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// ---------------------------------------------------------------------------
|
|
122
|
-
// Response mapping (credential-free, PII-stripped)
|
|
123
|
-
// ---------------------------------------------------------------------------
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Wire shape returned by `calendar.events.list`. Extends the shared operator
|
|
127
|
-
* `CalendarEventSummary` with the two fields the daemon handoff I/O contract
|
|
128
|
-
* requires on the summary (`description` and display-name-only `attendees`),
|
|
129
|
-
* which the base operator type leaves to its open `metadata` slot. Keeping them
|
|
130
|
-
* as first-class fields makes the response an exact match for the handoff
|
|
131
|
-
* `CalendarEventSummary` block rather than a partial subset.
|
|
132
|
-
*/
|
|
133
|
-
export interface CalendarEventSummaryResult extends CalendarEventSummary {
|
|
134
|
-
/** Free-text DESCRIPTION, when present. */
|
|
135
|
-
description?: string;
|
|
136
|
-
/** Display names only — never raw addresses. Omitted when there are none. */
|
|
137
|
-
attendees?: string[];
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function toSummary(event: CalDavEvent): CalendarEventSummaryResult {
|
|
141
|
-
const attendees = event.attendees.map((a) => a.displayName).filter((name) => name.length > 0);
|
|
142
|
-
return {
|
|
143
|
-
id: event.href || event.uid,
|
|
144
|
-
calendarId: event.calendarId,
|
|
145
|
-
title: event.summary,
|
|
146
|
-
start: event.start,
|
|
147
|
-
end: event.end,
|
|
148
|
-
allDay: event.allDay,
|
|
149
|
-
description: event.description,
|
|
150
|
-
location: event.location,
|
|
151
|
-
attendees: attendees.length > 0 ? attendees : undefined,
|
|
152
|
-
organizerDigest: event.organizerRaw ? sha256First(event.organizerRaw, 16) : undefined,
|
|
153
|
-
status: event.status,
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
export interface CalendarEventDetail {
|
|
158
|
-
id: string;
|
|
159
|
-
calendarId: string;
|
|
160
|
-
uid: string;
|
|
161
|
-
title: string;
|
|
162
|
-
start: string;
|
|
163
|
-
end: string;
|
|
164
|
-
allDay: boolean;
|
|
165
|
-
description?: string;
|
|
166
|
-
location?: string;
|
|
167
|
-
status?: string;
|
|
168
|
-
recurrence?: string;
|
|
169
|
-
/** Display names only — never raw addresses. */
|
|
170
|
-
attendees: string[];
|
|
171
|
-
organizerDigest?: string;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
function toDetail(event: CalDavEvent): CalendarEventDetail {
|
|
175
|
-
return {
|
|
176
|
-
id: event.href || event.uid,
|
|
177
|
-
calendarId: event.calendarId,
|
|
178
|
-
uid: event.uid,
|
|
179
|
-
title: event.summary,
|
|
180
|
-
start: event.start,
|
|
181
|
-
end: event.end,
|
|
182
|
-
allDay: event.allDay,
|
|
183
|
-
description: event.description,
|
|
184
|
-
location: event.location,
|
|
185
|
-
status: event.status,
|
|
186
|
-
recurrence: event.recurrence,
|
|
187
|
-
attendees: event.attendees.map((a) => a.displayName),
|
|
188
|
-
organizerDigest: event.organizerRaw ? sha256First(event.organizerRaw, 16) : undefined,
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// ---------------------------------------------------------------------------
|
|
193
|
-
// JSON Schemas
|
|
194
|
-
// ---------------------------------------------------------------------------
|
|
195
|
-
|
|
196
|
-
const calendarEventSummarySchema: Record<string, unknown> = {
|
|
197
|
-
type: 'object',
|
|
198
|
-
required: ['id', 'title', 'start', 'end'],
|
|
199
|
-
properties: {
|
|
200
|
-
id: { type: 'string' },
|
|
201
|
-
calendarId: { type: 'string' },
|
|
202
|
-
title: { type: 'string' },
|
|
203
|
-
start: { type: 'string', format: 'date-time' },
|
|
204
|
-
end: { type: 'string', format: 'date-time' },
|
|
205
|
-
allDay: { type: 'boolean' },
|
|
206
|
-
description: { type: 'string' },
|
|
207
|
-
location: { type: 'string' },
|
|
208
|
-
attendees: { type: 'array', items: { type: 'string' } },
|
|
209
|
-
organizerDigest: { type: 'string' },
|
|
210
|
-
status: { type: 'string' },
|
|
211
|
-
},
|
|
212
|
-
};
|
|
213
|
-
|
|
214
|
-
// ---------------------------------------------------------------------------
|
|
215
|
-
// Handlers
|
|
216
|
-
// ---------------------------------------------------------------------------
|
|
217
|
-
|
|
218
|
-
interface ListBody {
|
|
219
|
-
calendarId?: string;
|
|
220
|
-
from?: string;
|
|
221
|
-
to?: string;
|
|
222
|
-
limit?: number;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
interface GetBody {
|
|
226
|
-
eventId: string;
|
|
227
|
-
calendarId?: string;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
interface CreateBody {
|
|
231
|
-
title: string;
|
|
232
|
-
start: string;
|
|
233
|
-
end: string;
|
|
234
|
-
description?: string;
|
|
235
|
-
attendees?: string[];
|
|
236
|
-
location?: string;
|
|
237
|
-
calendarId?: string;
|
|
238
|
-
confirm: true;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
interface ImportBody {
|
|
242
|
-
icsContent: string;
|
|
243
|
-
calendarId?: string;
|
|
244
|
-
confirm: true;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
interface ExportBody {
|
|
248
|
-
calendarId?: string;
|
|
249
|
-
from?: string;
|
|
250
|
-
to?: string;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* Register all five calendar operator methods against the catalog. Returns a
|
|
255
|
-
* single Unregister that tears all of them down.
|
|
256
|
-
*/
|
|
257
|
-
export function registerCalendarMethods(
|
|
258
|
-
ctx: OperatorContext,
|
|
259
|
-
options: RegisterCalendarOptions = {},
|
|
260
|
-
): Unregister {
|
|
261
|
-
const clientFactory = options.clientFactory ?? realClientFactory;
|
|
262
|
-
const getClient = (): Promise<CalDavClient> => clientFactory(ctx);
|
|
263
|
-
|
|
264
|
-
const listHandler: OperatorHandler<unknown, { events: CalendarEventSummaryResult[] }> = async ({ body }) => {
|
|
265
|
-
const record = body === undefined || body === null ? {} : asRecord(body);
|
|
266
|
-
const input: ListBody = {
|
|
267
|
-
calendarId: optionalString(record, 'calendarId'),
|
|
268
|
-
from: optionalString(record, 'from'),
|
|
269
|
-
to: optionalString(record, 'to'),
|
|
270
|
-
limit: optionalNumber(record, 'limit'),
|
|
271
|
-
};
|
|
272
|
-
if (input.from) validateIsoDate(input.from, 'from');
|
|
273
|
-
if (input.to) validateIsoDate(input.to, 'to');
|
|
274
|
-
const limit = input.limit !== undefined ? Math.max(1, Math.min(200, Math.floor(input.limit))) : 20;
|
|
275
|
-
const client = await getClient();
|
|
276
|
-
const events = await client.listEvents({ ...input, limit });
|
|
277
|
-
return { events: events.map(toSummary) };
|
|
278
|
-
};
|
|
279
|
-
|
|
280
|
-
const getHandler: OperatorHandler<unknown, { event: CalendarEventDetail }> = async ({ body }) => {
|
|
281
|
-
const record = asRecord(body);
|
|
282
|
-
const input: GetBody = {
|
|
283
|
-
eventId: requiredString(record, 'eventId'),
|
|
284
|
-
calendarId: optionalString(record, 'calendarId'),
|
|
285
|
-
};
|
|
286
|
-
const client = await getClient();
|
|
287
|
-
const event = await client.getEvent(input.eventId, input.calendarId);
|
|
288
|
-
if (!event) {
|
|
289
|
-
throw new OperatorError(`Event not found: ${input.eventId}`, 'CALENDAR_NOT_FOUND', 404);
|
|
290
|
-
}
|
|
291
|
-
return { event: toDetail(event) };
|
|
292
|
-
};
|
|
293
|
-
|
|
294
|
-
const createHandler: OperatorHandler<unknown, { eventId: string; uid: string; createdAt: string }> = async ({ body }) => {
|
|
295
|
-
const record = asRecord(body);
|
|
296
|
-
const input: CreateBody = {
|
|
297
|
-
title: requiredString(record, 'title'),
|
|
298
|
-
start: validateIsoDate(requiredString(record, 'start'), 'start'),
|
|
299
|
-
end: validateIsoDate(requiredString(record, 'end'), 'end'),
|
|
300
|
-
description: optionalString(record, 'description'),
|
|
301
|
-
attendees: optionalStringArray(record, 'attendees'),
|
|
302
|
-
location: optionalString(record, 'location'),
|
|
303
|
-
calendarId: optionalString(record, 'calendarId'),
|
|
304
|
-
confirm: true,
|
|
305
|
-
};
|
|
306
|
-
if (new Date(input.end).getTime() < new Date(input.start).getTime()) {
|
|
307
|
-
throw new OperatorError("Field 'end' must not be before 'start'.", 'CALENDAR_BAD_INPUT', 400);
|
|
308
|
-
}
|
|
309
|
-
const client = await getClient();
|
|
310
|
-
const created = await client.createEvent({
|
|
311
|
-
title: input.title,
|
|
312
|
-
start: input.start,
|
|
313
|
-
end: input.end,
|
|
314
|
-
description: input.description,
|
|
315
|
-
attendees: input.attendees,
|
|
316
|
-
location: input.location,
|
|
317
|
-
calendarId: input.calendarId,
|
|
318
|
-
});
|
|
319
|
-
return { eventId: created.eventId, uid: created.uid, createdAt: created.createdAt };
|
|
320
|
-
};
|
|
321
|
-
|
|
322
|
-
const importHandler: OperatorHandler<unknown, { imported: number; eventIds: string[]; errors: string[] }> = async ({ body }) => {
|
|
323
|
-
const record = asRecord(body);
|
|
324
|
-
const input: ImportBody = {
|
|
325
|
-
icsContent: requiredString(record, 'icsContent'),
|
|
326
|
-
calendarId: optionalString(record, 'calendarId'),
|
|
327
|
-
confirm: true,
|
|
328
|
-
};
|
|
329
|
-
const client = await getClient();
|
|
330
|
-
return client.importIcs(input.icsContent, input.calendarId);
|
|
331
|
-
};
|
|
332
|
-
|
|
333
|
-
const exportHandler: OperatorHandler<unknown, { icsContent: string; eventCount: number }> = async ({ body }) => {
|
|
334
|
-
const record = body === undefined || body === null ? {} : asRecord(body);
|
|
335
|
-
const input: ExportBody = {
|
|
336
|
-
calendarId: optionalString(record, 'calendarId'),
|
|
337
|
-
from: optionalString(record, 'from'),
|
|
338
|
-
to: optionalString(record, 'to'),
|
|
339
|
-
};
|
|
340
|
-
if (input.from) validateIsoDate(input.from, 'from');
|
|
341
|
-
if (input.to) validateIsoDate(input.to, 'to');
|
|
342
|
-
const client = await getClient();
|
|
343
|
-
return client.exportIcs(input);
|
|
344
|
-
};
|
|
345
|
-
|
|
346
|
-
return declareOperatorMethods(ctx, [
|
|
347
|
-
{
|
|
348
|
-
descriptor: {
|
|
349
|
-
id: 'calendar.events.list',
|
|
350
|
-
title: 'List calendar events',
|
|
351
|
-
description: 'List events from a CalDAV calendar within an optional time range.',
|
|
352
|
-
category: CATEGORY,
|
|
353
|
-
source: 'daemon',
|
|
354
|
-
access: 'operator',
|
|
355
|
-
transport: TRANSPORT,
|
|
356
|
-
scopes: ['calendar:read'],
|
|
357
|
-
effect: 'read-only-network',
|
|
358
|
-
confirm: false,
|
|
359
|
-
inputSchema: {
|
|
360
|
-
type: 'object',
|
|
361
|
-
properties: {
|
|
362
|
-
calendarId: { type: 'string' },
|
|
363
|
-
from: { type: 'string', format: 'date-time' },
|
|
364
|
-
to: { type: 'string', format: 'date-time' },
|
|
365
|
-
limit: { type: 'number', minimum: 1, maximum: 200, default: 20 },
|
|
366
|
-
},
|
|
367
|
-
},
|
|
368
|
-
outputSchema: {
|
|
369
|
-
type: 'object',
|
|
370
|
-
required: ['events'],
|
|
371
|
-
properties: { events: { type: 'array', items: calendarEventSummarySchema } },
|
|
372
|
-
},
|
|
373
|
-
},
|
|
374
|
-
handler: listHandler as OperatorHandler<unknown, unknown>,
|
|
375
|
-
},
|
|
376
|
-
{
|
|
377
|
-
descriptor: {
|
|
378
|
-
id: 'calendar.events.get',
|
|
379
|
-
title: 'Get calendar event',
|
|
380
|
-
description: 'Fetch a single calendar event with attendees, recurrence, and iCal UID.',
|
|
381
|
-
category: CATEGORY,
|
|
382
|
-
source: 'daemon',
|
|
383
|
-
access: 'operator',
|
|
384
|
-
transport: TRANSPORT,
|
|
385
|
-
scopes: ['calendar:read'],
|
|
386
|
-
effect: 'read-only-network',
|
|
387
|
-
confirm: false,
|
|
388
|
-
inputSchema: {
|
|
389
|
-
type: 'object',
|
|
390
|
-
required: ['eventId'],
|
|
391
|
-
properties: {
|
|
392
|
-
eventId: { type: 'string' },
|
|
393
|
-
calendarId: { type: 'string' },
|
|
394
|
-
},
|
|
395
|
-
},
|
|
396
|
-
outputSchema: {
|
|
397
|
-
type: 'object',
|
|
398
|
-
required: ['event'],
|
|
399
|
-
properties: {
|
|
400
|
-
event: {
|
|
401
|
-
type: 'object',
|
|
402
|
-
required: ['id', 'uid', 'title', 'start', 'end', 'attendees'],
|
|
403
|
-
properties: {
|
|
404
|
-
id: { type: 'string' },
|
|
405
|
-
calendarId: { type: 'string' },
|
|
406
|
-
uid: { type: 'string' },
|
|
407
|
-
title: { type: 'string' },
|
|
408
|
-
start: { type: 'string', format: 'date-time' },
|
|
409
|
-
end: { type: 'string', format: 'date-time' },
|
|
410
|
-
allDay: { type: 'boolean' },
|
|
411
|
-
description: { type: 'string' },
|
|
412
|
-
location: { type: 'string' },
|
|
413
|
-
status: { type: 'string' },
|
|
414
|
-
recurrence: { type: 'string' },
|
|
415
|
-
attendees: { type: 'array', items: { type: 'string' } },
|
|
416
|
-
organizerDigest: { type: 'string' },
|
|
417
|
-
},
|
|
418
|
-
},
|
|
419
|
-
},
|
|
420
|
-
},
|
|
421
|
-
},
|
|
422
|
-
handler: getHandler as OperatorHandler<unknown, unknown>,
|
|
423
|
-
},
|
|
424
|
-
{
|
|
425
|
-
descriptor: {
|
|
426
|
-
id: 'calendar.events.create',
|
|
427
|
-
title: 'Create calendar event',
|
|
428
|
-
description: 'Create an event on the user’s CalDAV calendar. Requires explicit confirmation.',
|
|
429
|
-
category: CATEGORY,
|
|
430
|
-
source: 'daemon',
|
|
431
|
-
access: 'operator',
|
|
432
|
-
transport: TRANSPORT,
|
|
433
|
-
scopes: ['calendar:write'],
|
|
434
|
-
effect: 'confirmed-effect',
|
|
435
|
-
confirm: true,
|
|
436
|
-
inputSchema: {
|
|
437
|
-
type: 'object',
|
|
438
|
-
required: ['title', 'start', 'end', 'confirm'],
|
|
439
|
-
properties: {
|
|
440
|
-
title: { type: 'string' },
|
|
441
|
-
start: { type: 'string', format: 'date-time' },
|
|
442
|
-
end: { type: 'string', format: 'date-time' },
|
|
443
|
-
description: { type: 'string' },
|
|
444
|
-
attendees: { type: 'array', items: { type: 'string' } },
|
|
445
|
-
location: { type: 'string' },
|
|
446
|
-
calendarId: { type: 'string' },
|
|
447
|
-
confirm: { const: true },
|
|
448
|
-
},
|
|
449
|
-
},
|
|
450
|
-
outputSchema: {
|
|
451
|
-
type: 'object',
|
|
452
|
-
required: ['eventId', 'uid', 'createdAt'],
|
|
453
|
-
properties: {
|
|
454
|
-
eventId: { type: 'string' },
|
|
455
|
-
uid: { type: 'string' },
|
|
456
|
-
createdAt: { type: 'string', format: 'date-time' },
|
|
457
|
-
},
|
|
458
|
-
},
|
|
459
|
-
},
|
|
460
|
-
handler: createHandler as OperatorHandler<unknown, unknown>,
|
|
461
|
-
},
|
|
462
|
-
{
|
|
463
|
-
descriptor: {
|
|
464
|
-
id: 'calendar.ics.import',
|
|
465
|
-
title: 'Import .ics to calendar',
|
|
466
|
-
description: 'Import one or more events from a raw RFC 5545 .ics object. Requires confirmation.',
|
|
467
|
-
category: CATEGORY,
|
|
468
|
-
source: 'daemon',
|
|
469
|
-
access: 'operator',
|
|
470
|
-
transport: TRANSPORT,
|
|
471
|
-
scopes: ['calendar:write'],
|
|
472
|
-
effect: 'confirmed-effect',
|
|
473
|
-
confirm: true,
|
|
474
|
-
inputSchema: {
|
|
475
|
-
type: 'object',
|
|
476
|
-
required: ['icsContent', 'confirm'],
|
|
477
|
-
properties: {
|
|
478
|
-
icsContent: { type: 'string' },
|
|
479
|
-
calendarId: { type: 'string' },
|
|
480
|
-
confirm: { const: true },
|
|
481
|
-
},
|
|
482
|
-
},
|
|
483
|
-
outputSchema: {
|
|
484
|
-
type: 'object',
|
|
485
|
-
required: ['imported', 'eventIds', 'errors'],
|
|
486
|
-
properties: {
|
|
487
|
-
imported: { type: 'number' },
|
|
488
|
-
eventIds: { type: 'array', items: { type: 'string' } },
|
|
489
|
-
errors: { type: 'array', items: { type: 'string' } },
|
|
490
|
-
},
|
|
491
|
-
},
|
|
492
|
-
},
|
|
493
|
-
handler: importHandler as OperatorHandler<unknown, unknown>,
|
|
494
|
-
},
|
|
495
|
-
{
|
|
496
|
-
descriptor: {
|
|
497
|
-
id: 'calendar.ics.export',
|
|
498
|
-
title: 'Export calendar as .ics',
|
|
499
|
-
description: 'Export a CalDAV calendar (optionally within a time range) as an RFC 5545 .ics document.',
|
|
500
|
-
category: CATEGORY,
|
|
501
|
-
source: 'daemon',
|
|
502
|
-
access: 'operator',
|
|
503
|
-
transport: TRANSPORT,
|
|
504
|
-
scopes: ['calendar:read'],
|
|
505
|
-
effect: 'read-only',
|
|
506
|
-
confirm: false,
|
|
507
|
-
inputSchema: {
|
|
508
|
-
type: 'object',
|
|
509
|
-
properties: {
|
|
510
|
-
calendarId: { type: 'string' },
|
|
511
|
-
from: { type: 'string', format: 'date-time' },
|
|
512
|
-
to: { type: 'string', format: 'date-time' },
|
|
513
|
-
},
|
|
514
|
-
},
|
|
515
|
-
outputSchema: {
|
|
516
|
-
type: 'object',
|
|
517
|
-
required: ['icsContent', 'eventCount'],
|
|
518
|
-
properties: {
|
|
519
|
-
icsContent: { type: 'string' },
|
|
520
|
-
eventCount: { type: 'number' },
|
|
521
|
-
},
|
|
522
|
-
},
|
|
523
|
-
},
|
|
524
|
-
handler: exportHandler as OperatorHandler<unknown, unknown>,
|
|
525
|
-
},
|
|
526
|
-
]);
|
|
527
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
// Surface barrel: Draft Sync Backend (channels.drafts.*).
|
|
2
|
-
// Integration calls registerDraftsMethods(ctx) to publish the four methods.
|
|
3
|
-
|
|
4
|
-
export { registerDraftsMethods } from './register.ts';
|
|
5
|
-
export type { RegisterDraftsOptions } from './register.ts';
|
|
6
|
-
|
|
7
|
-
export { DraftSyncStore } from './draft-store.ts';
|
|
8
|
-
export type {
|
|
9
|
-
DraftRecord,
|
|
10
|
-
DraftStatus,
|
|
11
|
-
DraftSaveInput,
|
|
12
|
-
DraftSaveResult,
|
|
13
|
-
DraftListQuery,
|
|
14
|
-
DraftSyncStoreOptions,
|
|
15
|
-
} from './draft-store.ts';
|
|
16
|
-
export {
|
|
17
|
-
ALL_DRAFT_STATUSES,
|
|
18
|
-
WRITABLE_DRAFT_STATUSES,
|
|
19
|
-
DEFAULT_DRAFT_LIST_LIMIT,
|
|
20
|
-
MAX_DRAFT_LIST_LIMIT,
|
|
21
|
-
DRAFT_MESSAGE_DIGEST_HEX,
|
|
22
|
-
} from './draft-store.ts';
|