crewly 1.20.104 → 1.20.106
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/config/skills/agent/core/todo-add/SKILL.md +64 -0
- package/config/skills/agent/core/todo-add/execute.sh +84 -0
- package/config/skills/agent/core/todo-add/execute.test.sh +76 -0
- package/config/skills/agent/core/todo-lists/SKILL.md +58 -0
- package/config/skills/agent/core/todo-lists/execute.sh +70 -0
- package/config/skills/agent/core/todo-lists/execute.test.sh +77 -0
- package/config/skills/agent/core/todo-tasks/SKILL.md +59 -0
- package/config/skills/agent/core/todo-tasks/execute.sh +78 -0
- package/config/skills/agent/core/todo-tasks/execute.test.sh +74 -0
- package/config/skills/agent/core/todo-update/SKILL.md +63 -0
- package/config/skills/agent/core/todo-update/execute.sh +115 -0
- package/config/skills/agent/core/todo-update/execute.test.sh +82 -0
- package/dist/backend/backend/src/constants.d.ts +65 -0
- package/dist/backend/backend/src/constants.d.ts.map +1 -1
- package/dist/backend/backend/src/constants.js +65 -0
- package/dist/backend/backend/src/constants.js.map +1 -1
- package/dist/backend/backend/src/controllers/microsoft/microsoft-todo.controller.d.ts +51 -0
- package/dist/backend/backend/src/controllers/microsoft/microsoft-todo.controller.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/microsoft/microsoft-todo.controller.js +223 -0
- package/dist/backend/backend/src/controllers/microsoft/microsoft-todo.controller.js.map +1 -0
- package/dist/backend/backend/src/controllers/microsoft/microsoft-todo.routes.d.ts +25 -0
- package/dist/backend/backend/src/controllers/microsoft/microsoft-todo.routes.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/microsoft/microsoft-todo.routes.js +42 -0
- package/dist/backend/backend/src/controllers/microsoft/microsoft-todo.routes.js.map +1 -0
- package/dist/backend/backend/src/index.d.ts.map +1 -1
- package/dist/backend/backend/src/index.js +4 -9
- package/dist/backend/backend/src/index.js.map +1 -1
- package/dist/backend/backend/src/routes/api.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/routes/api.routes.js +3 -0
- package/dist/backend/backend/src/routes/api.routes.js.map +1 -1
- package/dist/backend/backend/src/services/agent/restore-filter.d.ts +37 -0
- package/dist/backend/backend/src/services/agent/restore-filter.d.ts.map +1 -0
- package/dist/backend/backend/src/services/agent/restore-filter.js +48 -0
- package/dist/backend/backend/src/services/agent/restore-filter.js.map +1 -0
- package/dist/backend/backend/src/services/connector/connector-access.service.d.ts +2 -2
- package/dist/backend/backend/src/services/connector/connector-access.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/connector/connector-access.service.js +2 -2
- package/dist/backend/backend/src/services/connector/connector-access.service.js.map +1 -1
- package/dist/backend/backend/src/services/microsoft/microsoft-todo.service.d.ts +250 -0
- package/dist/backend/backend/src/services/microsoft/microsoft-todo.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/microsoft/microsoft-todo.service.js +367 -0
- package/dist/backend/backend/src/services/microsoft/microsoft-todo.service.js.map +1 -0
- package/dist/backend/backend/src/services/microsoft/microsoft-token.service.d.ts +116 -0
- package/dist/backend/backend/src/services/microsoft/microsoft-token.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/microsoft/microsoft-token.service.js +220 -0
- package/dist/backend/backend/src/services/microsoft/microsoft-token.service.js.map +1 -0
- package/dist/backend/build-info.json +2 -2
- package/dist/cli/backend/src/constants.d.ts +65 -0
- package/dist/cli/backend/src/constants.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.js +65 -0
- package/dist/cli/backend/src/constants.js.map +1 -1
- package/frontend/dist/assets/{index-1fd45a2a.js → index-488339c1.js} +103 -103
- package/frontend/dist/index.html +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MicrosoftTodoService — Microsoft To Do over Graph v1.0 on the owner's grant.
|
|
3
|
+
*
|
|
4
|
+
* What agents can do: list task lists (and create one), list the tasks of a
|
|
5
|
+
* list (open ones by default), add a task (note, due date, importance),
|
|
6
|
+
* update a task (complete / reopen, retitle, re-date) and delete one.
|
|
7
|
+
*
|
|
8
|
+
* Lists are named by id or by display name (case-insensitive); no name at
|
|
9
|
+
* all means the owner's default list ("Tasks", `wellknownListName:
|
|
10
|
+
* defaultList`). Every call resolves the name against `GET /me/todo/lists`
|
|
11
|
+
* so the answer can say which list it acted on.
|
|
12
|
+
*
|
|
13
|
+
* Graph failures map to stable codes: 401 refreshes the token once, 429 is
|
|
14
|
+
* waited out once when `Retry-After` is short and otherwise returned as
|
|
15
|
+
* `rate_limited` with the delay, 403 → `forbidden`, 404 → `not_found`.
|
|
16
|
+
*
|
|
17
|
+
* @module services/microsoft/microsoft-todo.service
|
|
18
|
+
*/
|
|
19
|
+
import { MICROSOFT_TODO_CONSTANTS } from '../../constants.js';
|
|
20
|
+
import { MicrosoftError } from './microsoft-token.service.js';
|
|
21
|
+
const CODES = MICROSOFT_TODO_CONSTANTS.ERROR_CODES;
|
|
22
|
+
/** Upper bound on `@odata.nextLink` pages followed when listing lists. */
|
|
23
|
+
const MAX_LIST_PAGES = 10;
|
|
24
|
+
const HTTP_NO_CONTENT = 204;
|
|
25
|
+
const MS_PER_SECOND = 1000;
|
|
26
|
+
/**
|
|
27
|
+
* Trim a Graph `todoTaskList`.
|
|
28
|
+
*
|
|
29
|
+
* @param l - Wire list
|
|
30
|
+
* @returns Trimmed list
|
|
31
|
+
*/
|
|
32
|
+
export function toList(l) {
|
|
33
|
+
return {
|
|
34
|
+
id: l.id ?? '',
|
|
35
|
+
name: l.displayName ?? '',
|
|
36
|
+
...(l.wellknownListName === MICROSOFT_TODO_CONSTANTS.DEFAULT_LIST_WELLKNOWN ? { isDefault: true } : {}),
|
|
37
|
+
...(l.isShared ? { isShared: true } : {}),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Plain text of a task body (HTML bodies come from Outlook-created tasks).
|
|
42
|
+
*
|
|
43
|
+
* @param body - Wire body
|
|
44
|
+
* @returns Collapsed text, truncated to the preview length, or undefined
|
|
45
|
+
*/
|
|
46
|
+
export function noteText(body) {
|
|
47
|
+
let text = body?.content ?? '';
|
|
48
|
+
if ((body?.contentType ?? '').toLowerCase() === 'html') {
|
|
49
|
+
text = text.replace(/<[^>]*>/g, ' ').replace(/ /g, ' ').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
50
|
+
}
|
|
51
|
+
text = text.replace(/\s+/g, ' ').trim();
|
|
52
|
+
if (!text)
|
|
53
|
+
return undefined;
|
|
54
|
+
const max = MICROSOFT_TODO_CONSTANTS.NOTE_PREVIEW_LENGTH;
|
|
55
|
+
return text.length > max ? `${text.slice(0, max)}…` : text;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Trim a Graph `todoTask`.
|
|
59
|
+
*
|
|
60
|
+
* @param t - Wire task
|
|
61
|
+
* @returns Trimmed task
|
|
62
|
+
*/
|
|
63
|
+
export function toTask(t) {
|
|
64
|
+
const note = noteText(t.body);
|
|
65
|
+
const due = t.dueDateTime?.dateTime?.slice(0, 10);
|
|
66
|
+
const completedAt = t.status === 'completed' ? t.completedDateTime?.dateTime?.slice(0, 10) : undefined;
|
|
67
|
+
return {
|
|
68
|
+
id: t.id ?? '',
|
|
69
|
+
title: t.title ?? '',
|
|
70
|
+
status: t.status ?? 'notStarted',
|
|
71
|
+
...(t.importance && t.importance !== 'normal' ? { importance: t.importance } : {}),
|
|
72
|
+
...(due ? { due } : {}),
|
|
73
|
+
...(note ? { note } : {}),
|
|
74
|
+
...(completedAt ? { completedAt } : {}),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Validate a `YYYY-MM-DD` date and turn it into Graph's `dueDateTime`.
|
|
79
|
+
*
|
|
80
|
+
* @param value - Date text
|
|
81
|
+
* @returns `{ dateTime, timeZone }`
|
|
82
|
+
* @throws MicrosoftError(400, validation) for a malformed or impossible date
|
|
83
|
+
*/
|
|
84
|
+
export function toDueDateTime(value) {
|
|
85
|
+
const text = value.trim();
|
|
86
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(text);
|
|
87
|
+
const parsed = match ? new Date(Date.UTC(Number(match[1]), Number(match[2]) - 1, Number(match[3]))) : null;
|
|
88
|
+
if (!match || !parsed || parsed.toISOString().slice(0, 10) !== text) {
|
|
89
|
+
throw new MicrosoftError(400, CODES.VALIDATION, `due must be a real date as YYYY-MM-DD (got "${value}")`);
|
|
90
|
+
}
|
|
91
|
+
return { dateTime: `${text}T00:00:00`, timeZone: MICROSOFT_TODO_CONSTANTS.DUE_TIME_ZONE };
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Validate an importance value.
|
|
95
|
+
*
|
|
96
|
+
* @param value - Candidate
|
|
97
|
+
* @returns Lower-cased importance
|
|
98
|
+
* @throws MicrosoftError(400, validation) when unknown
|
|
99
|
+
*/
|
|
100
|
+
export function toImportance(value) {
|
|
101
|
+
const v = value.trim().toLowerCase();
|
|
102
|
+
if (!MICROSOFT_TODO_CONSTANTS.IMPORTANCE_VALUES.includes(v)) {
|
|
103
|
+
throw new MicrosoftError(400, CODES.VALIDATION, `importance must be one of ${MICROSOFT_TODO_CONSTANTS.IMPORTANCE_VALUES.join(', ')}`);
|
|
104
|
+
}
|
|
105
|
+
return v;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Pick the list a reference names: exact id, then case-insensitive name,
|
|
109
|
+
* then `default` (or nothing) → the default list.
|
|
110
|
+
*
|
|
111
|
+
* @param lists - All lists
|
|
112
|
+
* @param ref - Id or name (may be empty)
|
|
113
|
+
* @returns The list
|
|
114
|
+
* @throws MicrosoftError(404, not_found) when nothing matches; (400, validation) when a name is ambiguous
|
|
115
|
+
*/
|
|
116
|
+
export function pickList(lists, ref) {
|
|
117
|
+
const wanted = (ref ?? '').trim();
|
|
118
|
+
if (wanted) {
|
|
119
|
+
const byId = lists.find((l) => l.id === wanted);
|
|
120
|
+
if (byId)
|
|
121
|
+
return byId;
|
|
122
|
+
const byName = lists.filter((l) => l.name.trim().toLowerCase() === wanted.toLowerCase());
|
|
123
|
+
if (byName.length === 1)
|
|
124
|
+
return byName[0];
|
|
125
|
+
if (byName.length > 1) {
|
|
126
|
+
throw new MicrosoftError(400, CODES.VALIDATION, `Several lists are called "${wanted}"; pass the list id instead (${byName.map((l) => l.id).join(', ')})`);
|
|
127
|
+
}
|
|
128
|
+
if (wanted.toLowerCase() !== 'default') {
|
|
129
|
+
const names = lists.map((l) => l.name).join(', ');
|
|
130
|
+
throw new MicrosoftError(404, CODES.NOT_FOUND, `No To Do list called "${wanted}". Lists: ${names || '(none)'}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
const fallback = lists.find((l) => l.isDefault) ?? lists[0];
|
|
134
|
+
if (!fallback)
|
|
135
|
+
throw new MicrosoftError(404, CODES.NOT_FOUND, 'This Microsoft account has no To Do lists.');
|
|
136
|
+
return fallback;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Microsoft To Do calls.
|
|
140
|
+
*/
|
|
141
|
+
export class MicrosoftTodoService {
|
|
142
|
+
deps;
|
|
143
|
+
base = `${MICROSOFT_TODO_CONSTANTS.GRAPH_BASE}/me/todo/lists`;
|
|
144
|
+
/**
|
|
145
|
+
* @param deps - Token provider, fetch, sleep
|
|
146
|
+
*/
|
|
147
|
+
constructor(deps) {
|
|
148
|
+
this.deps = {
|
|
149
|
+
...deps,
|
|
150
|
+
fetchImpl: deps.fetchImpl ?? fetch,
|
|
151
|
+
sleep: deps.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms))),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* `GET /me/todo/lists` (following `@odata.nextLink`).
|
|
156
|
+
*
|
|
157
|
+
* @returns Every list
|
|
158
|
+
*/
|
|
159
|
+
async listLists() {
|
|
160
|
+
const out = [];
|
|
161
|
+
let url = this.base;
|
|
162
|
+
for (let page = 0; url && page < MAX_LIST_PAGES; page += 1) {
|
|
163
|
+
const data = await this.request(url);
|
|
164
|
+
out.push(...(data.value ?? []).map(toList));
|
|
165
|
+
url = data['@odata.nextLink'];
|
|
166
|
+
}
|
|
167
|
+
return out;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* `POST /me/todo/lists`.
|
|
171
|
+
*
|
|
172
|
+
* @param name - Display name
|
|
173
|
+
* @returns The new list
|
|
174
|
+
* @throws MicrosoftError(400, validation) for an empty name
|
|
175
|
+
*/
|
|
176
|
+
async createList(name) {
|
|
177
|
+
const displayName = (name ?? '').trim().slice(0, MICROSOFT_TODO_CONSTANTS.TITLE_MAX_LENGTH);
|
|
178
|
+
if (!displayName)
|
|
179
|
+
throw new MicrosoftError(400, CODES.VALIDATION, '"name" is required');
|
|
180
|
+
return toList(await this.request(this.base, { method: 'POST', body: { displayName } }));
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Resolve a list reference (id, name, `default`, or empty).
|
|
184
|
+
*
|
|
185
|
+
* @param ref - Reference
|
|
186
|
+
* @returns The list
|
|
187
|
+
*/
|
|
188
|
+
async resolveList(ref) {
|
|
189
|
+
return pickList(await this.listLists(), ref);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* `GET /me/todo/lists/{id}/tasks` — open tasks unless `includeCompleted`.
|
|
193
|
+
*
|
|
194
|
+
* @param input - List, completed flag, limit
|
|
195
|
+
* @returns The list, its tasks, and whether more exist
|
|
196
|
+
*/
|
|
197
|
+
async listTasks(input = {}) {
|
|
198
|
+
const list = await this.resolveList(input.list);
|
|
199
|
+
const limit = Math.min(Math.max(1, Math.floor(input.limit ?? MICROSOFT_TODO_CONSTANTS.TASKS_DEFAULT_LIMIT) || MICROSOFT_TODO_CONSTANTS.TASKS_DEFAULT_LIMIT), MICROSOFT_TODO_CONSTANTS.TASKS_LIMIT_CEILING);
|
|
200
|
+
const url = new URL(`${this.base}/${encodeURIComponent(list.id)}/tasks`);
|
|
201
|
+
url.searchParams.set('$top', String(limit));
|
|
202
|
+
if (!input.includeCompleted)
|
|
203
|
+
url.searchParams.set('$filter', "status ne 'completed'");
|
|
204
|
+
const data = await this.request(url.toString());
|
|
205
|
+
return { list: { id: list.id, name: list.name }, tasks: (data.value ?? []).map(toTask), hasMore: Boolean(data['@odata.nextLink']) };
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* `POST /me/todo/lists/{id}/tasks`.
|
|
209
|
+
*
|
|
210
|
+
* @param input - List, title, note, due, importance
|
|
211
|
+
* @returns The list and the new task
|
|
212
|
+
* @throws MicrosoftError(400, validation) for a missing title / bad due / bad importance
|
|
213
|
+
*/
|
|
214
|
+
async addTask(input) {
|
|
215
|
+
const title = (input.title ?? '').trim().slice(0, MICROSOFT_TODO_CONSTANTS.TITLE_MAX_LENGTH);
|
|
216
|
+
if (!title)
|
|
217
|
+
throw new MicrosoftError(400, CODES.VALIDATION, '"title" is required');
|
|
218
|
+
const body = { title };
|
|
219
|
+
const note = (input.note ?? '').trim();
|
|
220
|
+
if (note)
|
|
221
|
+
body.body = { content: note.slice(0, MICROSOFT_TODO_CONSTANTS.NOTE_MAX_LENGTH), contentType: 'text' };
|
|
222
|
+
if (input.due?.trim())
|
|
223
|
+
body.dueDateTime = toDueDateTime(input.due);
|
|
224
|
+
if (input.importance?.trim())
|
|
225
|
+
body.importance = toImportance(input.importance);
|
|
226
|
+
const list = await this.resolveList(input.list);
|
|
227
|
+
const task = await this.request(`${this.base}/${encodeURIComponent(list.id)}/tasks`, { method: 'POST', body });
|
|
228
|
+
return { list: { id: list.id, name: list.name }, task: toTask(task) };
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* `PATCH /me/todo/lists/{id}/tasks/{taskId}`.
|
|
232
|
+
*
|
|
233
|
+
* @param input - List, task id, and the fields to change
|
|
234
|
+
* @returns The list and the updated task
|
|
235
|
+
* @throws MicrosoftError(400, validation) when nothing is changed or a value is bad
|
|
236
|
+
*/
|
|
237
|
+
async updateTask(input) {
|
|
238
|
+
const taskId = (input.taskId ?? '').trim();
|
|
239
|
+
if (!taskId)
|
|
240
|
+
throw new MicrosoftError(400, CODES.VALIDATION, '"taskId" is required');
|
|
241
|
+
const body = {};
|
|
242
|
+
if (input.title !== undefined) {
|
|
243
|
+
const title = input.title.trim().slice(0, MICROSOFT_TODO_CONSTANTS.TITLE_MAX_LENGTH);
|
|
244
|
+
if (!title)
|
|
245
|
+
throw new MicrosoftError(400, CODES.VALIDATION, '"title" cannot be empty');
|
|
246
|
+
body.title = title;
|
|
247
|
+
}
|
|
248
|
+
if (input.note !== undefined)
|
|
249
|
+
body.body = { content: input.note.trim().slice(0, MICROSOFT_TODO_CONSTANTS.NOTE_MAX_LENGTH), contentType: 'text' };
|
|
250
|
+
if (input.due === null)
|
|
251
|
+
body.dueDateTime = null;
|
|
252
|
+
else if (input.due !== undefined)
|
|
253
|
+
body.dueDateTime = toDueDateTime(input.due);
|
|
254
|
+
if (input.importance !== undefined)
|
|
255
|
+
body.importance = toImportance(input.importance);
|
|
256
|
+
if (input.complete !== undefined)
|
|
257
|
+
body.status = input.complete ? 'completed' : 'notStarted';
|
|
258
|
+
if (Object.keys(body).length === 0) {
|
|
259
|
+
throw new MicrosoftError(400, CODES.VALIDATION, 'nothing to change: give complete, title, note, due or importance');
|
|
260
|
+
}
|
|
261
|
+
const list = await this.resolveList(input.list);
|
|
262
|
+
const task = await this.request(`${this.base}/${encodeURIComponent(list.id)}/tasks/${encodeURIComponent(taskId)}`, { method: 'PATCH', body });
|
|
263
|
+
return { list: { id: list.id, name: list.name }, task: toTask(task) };
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* `DELETE /me/todo/lists/{id}/tasks/{taskId}`.
|
|
267
|
+
*
|
|
268
|
+
* @param listRef - List id or name
|
|
269
|
+
* @param taskId - Task id
|
|
270
|
+
* @returns What was deleted
|
|
271
|
+
*/
|
|
272
|
+
async deleteTask(listRef, taskId) {
|
|
273
|
+
const id = (taskId ?? '').trim();
|
|
274
|
+
if (!id)
|
|
275
|
+
throw new MicrosoftError(400, CODES.VALIDATION, '"taskId" is required');
|
|
276
|
+
const list = await this.resolveList(listRef);
|
|
277
|
+
await this.request(`${this.base}/${encodeURIComponent(list.id)}/tasks/${encodeURIComponent(id)}`, { method: 'DELETE' });
|
|
278
|
+
return { list: { id: list.id, name: list.name }, taskId: id, deleted: true };
|
|
279
|
+
}
|
|
280
|
+
// -------------------------------------------------------------------------
|
|
281
|
+
// Internals
|
|
282
|
+
// -------------------------------------------------------------------------
|
|
283
|
+
/**
|
|
284
|
+
* Authenticated Graph call.
|
|
285
|
+
*
|
|
286
|
+
* 401 → drop the cached token and retry once; still 401 → `unauthorized`.
|
|
287
|
+
* 429 → wait out a short `Retry-After` once, else `rate_limited`.
|
|
288
|
+
* 400 → `validation`, 403 → `forbidden`, 404 → `not_found`, others → 502.
|
|
289
|
+
*
|
|
290
|
+
* @param url - Absolute URL
|
|
291
|
+
* @param init - Method / JSON body
|
|
292
|
+
* @returns Parsed JSON (`{}` for 204)
|
|
293
|
+
*/
|
|
294
|
+
async request(url, init = {}) {
|
|
295
|
+
let retriedAuth = false;
|
|
296
|
+
let retriedThrottle = false;
|
|
297
|
+
for (;;) {
|
|
298
|
+
const token = await this.deps.tokens.getAccessToken();
|
|
299
|
+
let res;
|
|
300
|
+
try {
|
|
301
|
+
res = await this.deps.fetchImpl(url, {
|
|
302
|
+
method: init.method ?? 'GET',
|
|
303
|
+
headers: {
|
|
304
|
+
Authorization: `Bearer ${token}`,
|
|
305
|
+
Accept: 'application/json',
|
|
306
|
+
...(init.body !== undefined ? { 'Content-Type': 'application/json' } : {}),
|
|
307
|
+
},
|
|
308
|
+
body: init.body !== undefined ? JSON.stringify(init.body) : undefined,
|
|
309
|
+
signal: AbortSignal.timeout(MICROSOFT_TODO_CONSTANTS.REQUEST_TIMEOUT_MS),
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
catch (err) {
|
|
313
|
+
throw new MicrosoftError(502, CODES.NETWORK, `Microsoft Graph unreachable: ${err instanceof Error ? err.message : String(err)}`);
|
|
314
|
+
}
|
|
315
|
+
if (res.status === HTTP_NO_CONTENT)
|
|
316
|
+
return {};
|
|
317
|
+
const text = await res.text();
|
|
318
|
+
if (res.ok) {
|
|
319
|
+
if (!text)
|
|
320
|
+
return {};
|
|
321
|
+
try {
|
|
322
|
+
return JSON.parse(text);
|
|
323
|
+
}
|
|
324
|
+
catch {
|
|
325
|
+
throw new MicrosoftError(502, CODES.MICROSOFT_ERROR, 'Microsoft Graph returned a non-JSON response.');
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
let message = `Microsoft Graph request failed (${res.status})`;
|
|
329
|
+
let graphCode = '';
|
|
330
|
+
try {
|
|
331
|
+
const parsed = JSON.parse(text);
|
|
332
|
+
graphCode = parsed.error?.code ?? '';
|
|
333
|
+
message = parsed.error?.message || graphCode || message;
|
|
334
|
+
}
|
|
335
|
+
catch {
|
|
336
|
+
if (text)
|
|
337
|
+
message = text.slice(0, 200);
|
|
338
|
+
}
|
|
339
|
+
const detail = graphCode && !message.includes(graphCode) ? `${graphCode}: ${message}` : message;
|
|
340
|
+
if (res.status === 401) {
|
|
341
|
+
this.deps.tokens.clearCache();
|
|
342
|
+
if (!retriedAuth) {
|
|
343
|
+
retriedAuth = true;
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
throw new MicrosoftError(401, CODES.UNAUTHORIZED, `Microsoft rejected the access token: ${detail}`);
|
|
347
|
+
}
|
|
348
|
+
if (res.status === 429) {
|
|
349
|
+
const retryAfter = Math.max(1, Number.parseInt(res.headers?.get?.('Retry-After') ?? '', 10) || 1);
|
|
350
|
+
if (!retriedThrottle && retryAfter <= MICROSOFT_TODO_CONSTANTS.RETRY_AFTER_MAX_WAIT_S) {
|
|
351
|
+
retriedThrottle = true;
|
|
352
|
+
await this.deps.sleep(retryAfter * MS_PER_SECOND);
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
355
|
+
throw new MicrosoftError(429, CODES.RATE_LIMITED, `Microsoft Graph is throttling requests; retry after ${retryAfter}s`, retryAfter);
|
|
356
|
+
}
|
|
357
|
+
if (res.status === 400)
|
|
358
|
+
throw new MicrosoftError(400, CODES.VALIDATION, detail);
|
|
359
|
+
if (res.status === 403)
|
|
360
|
+
throw new MicrosoftError(403, CODES.FORBIDDEN, detail);
|
|
361
|
+
if (res.status === 404)
|
|
362
|
+
throw new MicrosoftError(404, CODES.NOT_FOUND, detail);
|
|
363
|
+
throw new MicrosoftError(502, CODES.MICROSOFT_ERROR, detail);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
//# sourceMappingURL=microsoft-todo.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"microsoft-todo.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/microsoft/microsoft-todo.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MicrosoftTokenService — the Microsoft Graph access token for this
|
|
3
|
+
* instance, minted by Crewly Cloud from the owner's grant
|
|
4
|
+
* (`/api/cloud/microsoft/token`).
|
|
5
|
+
*
|
|
6
|
+
* Mirrors CanvaTokenService: Cloud keeps the client secret and the
|
|
7
|
+
* (rotating) refresh token; this side caches the access token until a
|
|
8
|
+
* minute before expiry and shares one in-flight refresh. The Cloud grant is
|
|
9
|
+
* keyed `microsoft`, so the same token will serve Outlook mail / calendar
|
|
10
|
+
* once those scopes are added — To Do is only its first consumer.
|
|
11
|
+
*
|
|
12
|
+
* @module services/microsoft/microsoft-token.service
|
|
13
|
+
*/
|
|
14
|
+
/** The slice of the Cloud client the service needs (injectable for tests). */
|
|
15
|
+
export interface MicrosoftCloudClient {
|
|
16
|
+
isConnected(): boolean;
|
|
17
|
+
getToken(): string | null;
|
|
18
|
+
getCloudUrl(): string | null;
|
|
19
|
+
}
|
|
20
|
+
/** Constructor dependencies. */
|
|
21
|
+
export interface MicrosoftTokenServiceDeps {
|
|
22
|
+
cloud?: MicrosoftCloudClient;
|
|
23
|
+
fetchImpl?: typeof fetch;
|
|
24
|
+
now?: () => number;
|
|
25
|
+
}
|
|
26
|
+
/** `GET /api/microsoft-todo/status` payload. */
|
|
27
|
+
export interface MicrosoftStatus {
|
|
28
|
+
connected: boolean;
|
|
29
|
+
cloudConnected: boolean;
|
|
30
|
+
microsoftUserId?: string;
|
|
31
|
+
displayName?: string;
|
|
32
|
+
email?: string;
|
|
33
|
+
scopes?: string[];
|
|
34
|
+
grantedAt?: string;
|
|
35
|
+
}
|
|
36
|
+
/** A Microsoft / To Do failure with the HTTP status the controller answers with. */
|
|
37
|
+
export declare class MicrosoftError extends Error {
|
|
38
|
+
readonly status: number;
|
|
39
|
+
readonly code: string;
|
|
40
|
+
/** Seconds to wait before retrying (Graph 429 `Retry-After`). */
|
|
41
|
+
readonly retryAfterSeconds?: number | undefined;
|
|
42
|
+
constructor(status: number, code: string, message: string,
|
|
43
|
+
/** Seconds to wait before retrying (Graph 429 `Retry-After`). */
|
|
44
|
+
retryAfterSeconds?: number | undefined);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Map a Cloud grant-endpoint failure onto this instance's error codes.
|
|
48
|
+
*
|
|
49
|
+
* @param httpStatus - Cloud HTTP status
|
|
50
|
+
* @param code - Cloud `error` / `code`
|
|
51
|
+
* @param message - Cloud message
|
|
52
|
+
* @returns The mapped error
|
|
53
|
+
*/
|
|
54
|
+
export declare function mapCloudFailure(httpStatus: number, code: string | undefined, message: string | undefined): MicrosoftError;
|
|
55
|
+
/**
|
|
56
|
+
* Access-token provider for Microsoft Graph.
|
|
57
|
+
*/
|
|
58
|
+
export declare class MicrosoftTokenService {
|
|
59
|
+
private static instance;
|
|
60
|
+
private readonly logger;
|
|
61
|
+
private readonly cloud;
|
|
62
|
+
private readonly fetchImpl;
|
|
63
|
+
private readonly nowFn;
|
|
64
|
+
private cached;
|
|
65
|
+
private inflight;
|
|
66
|
+
constructor(deps?: MicrosoftTokenServiceDeps);
|
|
67
|
+
/**
|
|
68
|
+
* Process-wide instance.
|
|
69
|
+
*
|
|
70
|
+
* @returns The singleton
|
|
71
|
+
*/
|
|
72
|
+
static getInstance(): MicrosoftTokenService;
|
|
73
|
+
/** Reset the singleton (tests). */
|
|
74
|
+
static resetInstance(): void;
|
|
75
|
+
/**
|
|
76
|
+
* Whether this instance is signed in to Crewly Cloud.
|
|
77
|
+
*
|
|
78
|
+
* @returns True when a Cloud session is available
|
|
79
|
+
*/
|
|
80
|
+
isCloudAvailable(): boolean;
|
|
81
|
+
/**
|
|
82
|
+
* A usable access token (cached until 60 s before expiry).
|
|
83
|
+
*
|
|
84
|
+
* @returns The token
|
|
85
|
+
* @throws MicrosoftError not_logged_in / not_connected / not_configured / microsoft_error / network
|
|
86
|
+
*/
|
|
87
|
+
getAccessToken(): Promise<string>;
|
|
88
|
+
/** Forget the cached token (after a Graph 401 or a disconnect). */
|
|
89
|
+
clearCache(): void;
|
|
90
|
+
/**
|
|
91
|
+
* Whether Cloud holds a Microsoft grant; "not there" cases come back as
|
|
92
|
+
* `connected: false`, outages throw.
|
|
93
|
+
*
|
|
94
|
+
* @returns Status
|
|
95
|
+
*/
|
|
96
|
+
status(): Promise<MicrosoftStatus>;
|
|
97
|
+
/**
|
|
98
|
+
* Ask Cloud to forget the grant.
|
|
99
|
+
*
|
|
100
|
+
* @returns Whether Cloud had one
|
|
101
|
+
*/
|
|
102
|
+
disconnect(): Promise<{
|
|
103
|
+
removed: boolean;
|
|
104
|
+
}>;
|
|
105
|
+
/**
|
|
106
|
+
* The Cloud consent-start URL for the browser.
|
|
107
|
+
*
|
|
108
|
+
* @param returnUrl - Absolute dashboard URL Cloud redirects back to
|
|
109
|
+
* @returns URL to open
|
|
110
|
+
* @throws MicrosoftError(401, not_logged_in) when not signed in to Cloud
|
|
111
|
+
*/
|
|
112
|
+
buildConnectUrl(returnUrl: string): string;
|
|
113
|
+
private refresh;
|
|
114
|
+
private cloudRequest;
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=microsoft-token.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"microsoft-token.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/microsoft/microsoft-token.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,8EAA8E;AAC9E,MAAM,WAAW,oBAAoB;IACnC,WAAW,IAAI,OAAO,CAAC;IACvB,QAAQ,IAAI,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,IAAI,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,gCAAgC;AAChC,MAAM,WAAW,yBAAyB;IACxC,KAAK,CAAC,EAAE,oBAAoB,CAAC;IAC7B,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED,gDAAgD;AAChD,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,cAAc,EAAE,OAAO,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAoBD,oFAAoF;AACpF,qBAAa,cAAe,SAAQ,KAAK;aAErB,MAAM,EAAE,MAAM;aACd,IAAI,EAAE,MAAM;IAE5B,iEAAiE;aACjD,iBAAiB,CAAC,EAAE,MAAM;gBAJ1B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EAC5B,OAAO,EAAE,MAAM;IACf,iEAAiE;IACjD,iBAAiB,CAAC,EAAE,MAAM,YAAA;CAK7C;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,cAAc,CAezH;AAED;;GAEG;AACH,qBAAa,qBAAqB;IAChC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAsC;IAC7D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IACzC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAuB;IAC7C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IACrC,OAAO,CAAC,MAAM,CAA6D;IAC3E,OAAO,CAAC,QAAQ,CAAgC;gBAEpC,IAAI,GAAE,yBAA8B;IAOhD;;;;OAIG;IACH,MAAM,CAAC,WAAW,IAAI,qBAAqB;IAK3C,mCAAmC;IACnC,MAAM,CAAC,aAAa,IAAI,IAAI;IAI5B;;;;OAIG;IACH,gBAAgB,IAAI,OAAO;IAI3B;;;;;OAKG;IACG,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC;IASvC,mEAAmE;IACnE,UAAU,IAAI,IAAI;IAIlB;;;;;OAKG;IACG,MAAM,IAAI,OAAO,CAAC,eAAe,CAAC;IAuBxC;;;;OAIG;IACG,UAAU,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;IAMjD;;;;;;OAMG;IACH,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;YAY5B,OAAO;YAcP,YAAY;CA2B3B"}
|