@aligndottech/cli 0.1.4 → 0.3.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/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +43 -17
- package/dist/commands/setup.js.map +1 -1
- package/dist/lib/fetchers/confluence.d.ts +2 -0
- package/dist/lib/fetchers/confluence.d.ts.map +1 -1
- package/dist/lib/fetchers/confluence.js +9 -77
- package/dist/lib/fetchers/confluence.js.map +1 -1
- package/dist/lib/fetchers/git.d.ts +2 -0
- package/dist/lib/fetchers/git.d.ts.map +1 -1
- package/dist/lib/fetchers/git.js +5 -14
- package/dist/lib/fetchers/git.js.map +1 -1
- package/dist/lib/fetchers/github.d.ts +1 -0
- package/dist/lib/fetchers/github.d.ts.map +1 -1
- package/dist/lib/fetchers/github.js +3 -42
- package/dist/lib/fetchers/github.js.map +1 -1
- package/dist/lib/fetchers/gitlab.d.ts +1 -0
- package/dist/lib/fetchers/gitlab.d.ts.map +1 -1
- package/dist/lib/fetchers/gitlab.js +3 -21
- package/dist/lib/fetchers/gitlab.js.map +1 -1
- package/dist/lib/fetchers/jira.d.ts +2 -0
- package/dist/lib/fetchers/jira.d.ts.map +1 -1
- package/dist/lib/fetchers/jira.js +10 -55
- package/dist/lib/fetchers/jira.js.map +1 -1
- package/dist/lib/fetchers/linear.d.ts +1 -0
- package/dist/lib/fetchers/linear.d.ts.map +1 -1
- package/dist/lib/fetchers/linear.js +3 -60
- package/dist/lib/fetchers/linear.js.map +1 -1
- package/dist/lib/fetchers/notion.d.ts +1 -0
- package/dist/lib/fetchers/notion.d.ts.map +1 -1
- package/dist/lib/fetchers/notion.js +3 -73
- package/dist/lib/fetchers/notion.js.map +1 -1
- package/dist/lib/fetchers/slack.d.ts +1 -0
- package/dist/lib/fetchers/slack.d.ts.map +1 -1
- package/dist/lib/fetchers/slack.js +3 -85
- package/dist/lib/fetchers/slack.js.map +1 -1
- package/dist/lib/fetchers/teams.d.ts +1 -0
- package/dist/lib/fetchers/teams.d.ts.map +1 -1
- package/dist/lib/fetchers/teams.js +3 -62
- package/dist/lib/fetchers/teams.js.map +1 -1
- package/dist/lib/fetchers/zoom.d.ts +1 -0
- package/dist/lib/fetchers/zoom.d.ts.map +1 -1
- package/dist/lib/fetchers/zoom.js +3 -62
- package/dist/lib/fetchers/zoom.js.map +1 -1
- package/dist/lib/gateway-client.d.ts +2 -15
- package/dist/lib/gateway-client.d.ts.map +1 -1
- package/dist/lib/gateway-client.js.map +1 -1
- package/package.json +2 -1
|
@@ -1,60 +1,15 @@
|
|
|
1
|
+
import { FetcherAuthError, JiraFetcher } from '@aligndottech/connector-core';
|
|
1
2
|
import { AuthExpiredError } from '../errors.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
return '';
|
|
5
|
-
return (adf.content ?? [])
|
|
6
|
-
.flatMap(block => (block.content ?? []).map(inline => inline.text ?? ''))
|
|
7
|
-
.join(' ')
|
|
8
|
-
.trim();
|
|
9
|
-
}
|
|
3
|
+
/** Read-only personal Jira import. Delegates to the canonical fetcher in
|
|
4
|
+
* @aligndottech/connector-core; maps its auth error to the CLI's reconnect flow. */
|
|
10
5
|
export async function fetchJiraItems(opts) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const limit = opts.limit ?? 100;
|
|
19
|
-
const jql = 'assignee = currentUser() OR reporter = currentUser() ORDER BY updated DESC';
|
|
20
|
-
const res = await fetch(`${base}/rest/api/3/search/jql`, {
|
|
21
|
-
method: 'POST',
|
|
22
|
-
headers: { ...headers, 'Content-Type': 'application/json' },
|
|
23
|
-
body: JSON.stringify({ jql, maxResults: limit, fields: ['summary', 'description', 'status', 'key', 'reporter'] }),
|
|
24
|
-
});
|
|
25
|
-
if (!res.ok) {
|
|
26
|
-
// 401 = expired/revoked (re-auth helps). 403 = lacks Jira scopes / no access
|
|
27
|
-
// (re-auth won't help) - don't trigger a reconnect loop.
|
|
28
|
-
if (res.status === 401)
|
|
29
|
-
throw new AuthExpiredError('Jira');
|
|
30
|
-
if (res.status === 403) {
|
|
31
|
-
throw new Error("Jira access denied (403): the token lacks Jira scopes or access. Re-auth won't help - " +
|
|
32
|
-
"check the Atlassian app's Jira API permissions.");
|
|
33
|
-
}
|
|
34
|
-
const text = await res.text();
|
|
35
|
-
throw new Error(`Jira API failed (${res.status}): ${text.slice(0, 200)}`);
|
|
6
|
+
try {
|
|
7
|
+
return await new JiraFetcher().fetch(opts);
|
|
8
|
+
}
|
|
9
|
+
catch (e) {
|
|
10
|
+
if (e instanceof FetcherAuthError)
|
|
11
|
+
throw new AuthExpiredError(e.connector);
|
|
12
|
+
throw e;
|
|
36
13
|
}
|
|
37
|
-
const data = await res.json();
|
|
38
|
-
// For OAuth mode use the human site base URL (e.g. https://company.atlassian.net)
|
|
39
|
-
const browseBase = isOAuth
|
|
40
|
-
? (opts.siteBase ?? `https://api.atlassian.com/ex/jira/${opts.cloudId}`)
|
|
41
|
-
: base;
|
|
42
|
-
return data.issues.map(issue => {
|
|
43
|
-
const desc = extractAdfText(issue.fields.description);
|
|
44
|
-
return {
|
|
45
|
-
source_url: `${browseBase}/browse/${issue.key}`,
|
|
46
|
-
platform: 'jira',
|
|
47
|
-
raw_text: [
|
|
48
|
-
`[${issue.key}] ${issue.fields.summary}`,
|
|
49
|
-
desc,
|
|
50
|
-
issue.fields.status?.name ? `Status: ${issue.fields.status.name}` : '',
|
|
51
|
-
].filter(Boolean).join('\n\n'),
|
|
52
|
-
title: `[${issue.key}] ${issue.fields.summary}`,
|
|
53
|
-
// Who to talk to (ALI-118): the issue reporter.
|
|
54
|
-
...(issue.fields.reporter?.displayName
|
|
55
|
-
? { author: { name: issue.fields.reporter.displayName, ...(issue.fields.reporter.emailAddress ? { email: issue.fields.reporter.emailAddress } : {}) } }
|
|
56
|
-
: {}),
|
|
57
|
-
};
|
|
58
|
-
});
|
|
59
14
|
}
|
|
60
15
|
//# sourceMappingURL=jira.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jira.js","sourceRoot":"","sources":["../../../src/lib/fetchers/jira.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,
|
|
1
|
+
{"version":3,"file":"jira.js","sourceRoot":"","sources":["../../../src/lib/fetchers/jira.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGhD;qFACqF;AACrF,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAOpC;IACC,IAAI,CAAC;QACH,OAAO,MAAM,IAAI,WAAW,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC7C,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,CAAC,YAAY,gBAAgB;YAAE,MAAM,IAAI,gBAAgB,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QAC3E,MAAM,CAAC,CAAC;IACV,CAAC;AACH,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"linear.d.ts","sourceRoot":"","sources":["../../../src/lib/fetchers/linear.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"linear.d.ts","sourceRoot":"","sources":["../../../src/lib/fetchers/linear.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAEhE,8EAA8E;AAC9E,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAE7G"}
|
|
@@ -1,63 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
query PersonalIssues($first: Int!) {
|
|
4
|
-
viewer {
|
|
5
|
-
assignedIssues(first: $first, orderBy: updatedAt) {
|
|
6
|
-
nodes {
|
|
7
|
-
id title description url
|
|
8
|
-
state { name }
|
|
9
|
-
team { name }
|
|
10
|
-
creator { name email }
|
|
11
|
-
comments { nodes { body user { name } } }
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
createdIssues(first: $first, orderBy: updatedAt) {
|
|
15
|
-
nodes { id title description url state { name } team { name } creator { name email } }
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
}`;
|
|
1
|
+
import { LinearFetcher } from '@aligndottech/connector-core';
|
|
2
|
+
/** Read-only personal Linear import (canonical fetcher in connector-core). */
|
|
19
3
|
export async function fetchLinearItems(opts) {
|
|
20
|
-
|
|
21
|
-
const res = await fetch(LINEAR_GQL, {
|
|
22
|
-
method: 'POST',
|
|
23
|
-
headers: { Authorization: `Bearer ${opts.token}`, 'Content-Type': 'application/json' },
|
|
24
|
-
body: JSON.stringify({ query: ISSUES_QUERY, variables: { first: limit } }),
|
|
25
|
-
});
|
|
26
|
-
if (!res.ok)
|
|
27
|
-
throw new Error(`Linear API failed (${res.status}). Check your personal API token.`);
|
|
28
|
-
const data = await res.json();
|
|
29
|
-
if (data.errors?.length)
|
|
30
|
-
throw new Error(data.errors[0].message);
|
|
31
|
-
const seen = new Set();
|
|
32
|
-
const items = [];
|
|
33
|
-
const allIssues = [
|
|
34
|
-
...(data.data.viewer.assignedIssues?.nodes ?? []),
|
|
35
|
-
...(data.data.viewer.createdIssues?.nodes ?? []),
|
|
36
|
-
];
|
|
37
|
-
for (const issue of allIssues) {
|
|
38
|
-
if (seen.has(issue.id))
|
|
39
|
-
continue;
|
|
40
|
-
seen.add(issue.id);
|
|
41
|
-
const comments = (issue.comments?.nodes ?? [])
|
|
42
|
-
.map(c => `${c.user?.name ?? 'Unknown'}: ${c.body}`)
|
|
43
|
-
.join('\n');
|
|
44
|
-
items.push({
|
|
45
|
-
source_url: issue.url,
|
|
46
|
-
platform: 'linear',
|
|
47
|
-
raw_text: [
|
|
48
|
-
issue.title,
|
|
49
|
-
issue.description ?? '',
|
|
50
|
-
issue.team?.name ? `Team: ${issue.team.name}` : '',
|
|
51
|
-
issue.state?.name ? `Status: ${issue.state.name}` : '',
|
|
52
|
-
comments ? `Comments:\n${comments}` : '',
|
|
53
|
-
].filter(Boolean).join('\n\n'),
|
|
54
|
-
title: issue.title,
|
|
55
|
-
// Who to talk to (ALI-118): the issue creator.
|
|
56
|
-
...(issue.creator?.name
|
|
57
|
-
? { author: { name: issue.creator.name, ...(issue.creator.email ? { email: issue.creator.email } : {}) } }
|
|
58
|
-
: {}),
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
return items.slice(0, limit);
|
|
4
|
+
return new LinearFetcher().fetch(opts);
|
|
62
5
|
}
|
|
63
6
|
//# sourceMappingURL=linear.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"linear.js","sourceRoot":"","sources":["../../../src/lib/fetchers/linear.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"linear.js","sourceRoot":"","sources":["../../../src/lib/fetchers/linear.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAG7D,8EAA8E;AAC9E,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,IAAuC;IAC5E,OAAO,IAAI,aAAa,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AACzC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notion.d.ts","sourceRoot":"","sources":["../../../src/lib/fetchers/notion.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"notion.d.ts","sourceRoot":"","sources":["../../../src/lib/fetchers/notion.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAEhE,8EAA8E;AAC9E,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAE7G"}
|
|
@@ -1,76 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const cache = new Map();
|
|
4
|
-
return async (userId) => {
|
|
5
|
-
if (!userId)
|
|
6
|
-
return undefined;
|
|
7
|
-
if (cache.has(userId))
|
|
8
|
-
return cache.get(userId) ?? undefined;
|
|
9
|
-
try {
|
|
10
|
-
const res = await fetch(`https://api.notion.com/v1/users/${userId}`, { headers });
|
|
11
|
-
if (!res.ok) {
|
|
12
|
-
cache.set(userId, null);
|
|
13
|
-
return undefined;
|
|
14
|
-
}
|
|
15
|
-
const u = (await res.json());
|
|
16
|
-
const resolved = u.name ? { name: u.name, ...(u.person?.email ? { email: u.person.email } : {}) } : null;
|
|
17
|
-
cache.set(userId, resolved);
|
|
18
|
-
return resolved ?? undefined;
|
|
19
|
-
}
|
|
20
|
-
catch {
|
|
21
|
-
cache.set(userId, null);
|
|
22
|
-
return undefined;
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
function extractPageTitle(page) {
|
|
27
|
-
return (page.properties?.title?.title?.[0]?.plain_text ??
|
|
28
|
-
page.properties?.Name?.title?.[0]?.plain_text ??
|
|
29
|
-
'Untitled');
|
|
30
|
-
}
|
|
31
|
-
function extractBlockText(block) {
|
|
32
|
-
const content = block[block.type];
|
|
33
|
-
return (content?.rich_text ?? []).map(t => t.plain_text ?? '').join('');
|
|
34
|
-
}
|
|
1
|
+
import { NotionFetcher } from '@aligndottech/connector-core';
|
|
2
|
+
/** Read-only personal Notion import (canonical fetcher in connector-core). */
|
|
35
3
|
export async function fetchNotionItems(opts) {
|
|
36
|
-
|
|
37
|
-
Authorization: `Bearer ${opts.token}`,
|
|
38
|
-
'Notion-Version': '2022-06-28',
|
|
39
|
-
'Content-Type': 'application/json',
|
|
40
|
-
};
|
|
41
|
-
const limit = opts.limit ?? 50;
|
|
42
|
-
const searchRes = await fetch('https://api.notion.com/v1/search', {
|
|
43
|
-
method: 'POST',
|
|
44
|
-
headers,
|
|
45
|
-
body: JSON.stringify({ filter: { value: 'page', property: 'object' }, page_size: limit }),
|
|
46
|
-
});
|
|
47
|
-
if (!searchRes.ok)
|
|
48
|
-
throw new Error(`Notion API failed (${searchRes.status}). Check your integration token.`);
|
|
49
|
-
const data = await searchRes.json();
|
|
50
|
-
const resolveUser = makeNotionUserResolver(headers);
|
|
51
|
-
const items = [];
|
|
52
|
-
for (const page of data.results) {
|
|
53
|
-
const title = extractPageTitle(page);
|
|
54
|
-
const pageUrl = page.url ?? `https://notion.so/${page.id.replace(/-/g, '')}`;
|
|
55
|
-
// Who to talk to (ALI-119): the page creator.
|
|
56
|
-
const author = await resolveUser(page.created_by?.id);
|
|
57
|
-
let bodyText = '';
|
|
58
|
-
try {
|
|
59
|
-
const blocksRes = await fetch(`https://api.notion.com/v1/blocks/${page.id}/children?page_size=50`, { headers });
|
|
60
|
-
if (blocksRes.ok) {
|
|
61
|
-
const blocks = await blocksRes.json();
|
|
62
|
-
bodyText = blocks.results.map(extractBlockText).filter(Boolean).join('\n');
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
catch { /* skip block fetch errors */ }
|
|
66
|
-
items.push({
|
|
67
|
-
source_url: pageUrl,
|
|
68
|
-
platform: 'notion',
|
|
69
|
-
raw_text: [title, bodyText].filter(Boolean).join('\n\n').slice(0, 3000),
|
|
70
|
-
title,
|
|
71
|
-
...(author ? { author } : {}),
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
return items;
|
|
4
|
+
return new NotionFetcher().fetch(opts);
|
|
75
5
|
}
|
|
76
6
|
//# sourceMappingURL=notion.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notion.js","sourceRoot":"","sources":["../../../src/lib/fetchers/notion.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"notion.js","sourceRoot":"","sources":["../../../src/lib/fetchers/notion.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAG7D,8EAA8E;AAC9E,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,IAAuC;IAC5E,OAAO,IAAI,aAAa,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AACzC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slack.d.ts","sourceRoot":"","sources":["../../../src/lib/fetchers/slack.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"slack.d.ts","sourceRoot":"","sources":["../../../src/lib/fetchers/slack.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAEhE,6EAA6E;AAC7E,wBAAsB,eAAe,CAAC,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAE/H"}
|
|
@@ -1,88 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const res = await fetch(`https://slack.com/api/${endpoint}?${qs}`, {
|
|
4
|
-
headers: { Authorization: `Bearer ${token}` },
|
|
5
|
-
});
|
|
6
|
-
const data = await res.json();
|
|
7
|
-
if (!data.ok)
|
|
8
|
-
throw new Error(`Slack API error on ${endpoint}: ${data.error}`);
|
|
9
|
-
return data;
|
|
10
|
-
}
|
|
11
|
-
/** Resolve a Slack user id to a display name (cached - one users.info call per unique user). */
|
|
12
|
-
function makeUserResolver(token) {
|
|
13
|
-
const cache = new Map();
|
|
14
|
-
return async (userId) => {
|
|
15
|
-
if (!userId)
|
|
16
|
-
return undefined;
|
|
17
|
-
if (cache.has(userId))
|
|
18
|
-
return cache.get(userId) ?? undefined;
|
|
19
|
-
try {
|
|
20
|
-
const data = await slackGet('users.info', token, { user: userId });
|
|
21
|
-
const u = (data.user ?? {});
|
|
22
|
-
const name = u.profile?.real_name || u.real_name || u.profile?.display_name || u.name || userId;
|
|
23
|
-
const resolved = { name, ...(u.name ? { handle: u.name } : {}), ...(u.profile?.email ? { email: u.profile.email } : {}) };
|
|
24
|
-
cache.set(userId, resolved);
|
|
25
|
-
return resolved;
|
|
26
|
-
}
|
|
27
|
-
catch {
|
|
28
|
-
cache.set(userId, null); // don't retry a failed lookup
|
|
29
|
-
return undefined;
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
}
|
|
1
|
+
import { SlackFetcher } from '@aligndottech/connector-core';
|
|
2
|
+
/** Read-only personal Slack import (canonical fetcher in connector-core). */
|
|
33
3
|
export async function fetchSlackItems(opts) {
|
|
34
|
-
|
|
35
|
-
const daysBack = opts.daysBack ?? 90;
|
|
36
|
-
const oldest = String(Math.floor(Date.now() / 1000) - daysBack * 86400);
|
|
37
|
-
await slackGet('auth.test', opts.token);
|
|
38
|
-
const chanData = await slackGet('conversations.list', opts.token, {
|
|
39
|
-
types: 'public_channel,private_channel',
|
|
40
|
-
exclude_archived: 'true',
|
|
41
|
-
limit: '100',
|
|
42
|
-
});
|
|
43
|
-
const channels = chanData.channels ?? [];
|
|
44
|
-
const resolveUser = makeUserResolver(opts.token);
|
|
45
|
-
const items = [];
|
|
46
|
-
for (let channelIndex = 0; channelIndex < channels.length; channelIndex++) {
|
|
47
|
-
const channel = channels[channelIndex];
|
|
48
|
-
if (items.length >= limit)
|
|
49
|
-
break;
|
|
50
|
-
try {
|
|
51
|
-
// 3-second delay between channels to stay under Tier 2 rate limit (20 req/min)
|
|
52
|
-
if (channelIndex > 0)
|
|
53
|
-
await new Promise(r => setTimeout(r, 3000));
|
|
54
|
-
const hist = await slackGet('conversations.history', opts.token, {
|
|
55
|
-
channel: channel.id,
|
|
56
|
-
oldest,
|
|
57
|
-
limit: '100',
|
|
58
|
-
});
|
|
59
|
-
const messages = hist.messages ?? [];
|
|
60
|
-
const threads = messages.filter(m => (m.reply_count ?? 0) >= 2);
|
|
61
|
-
for (const thread of threads) {
|
|
62
|
-
if (items.length >= limit)
|
|
63
|
-
break;
|
|
64
|
-
try {
|
|
65
|
-
const replies = await slackGet('conversations.replies', opts.token, {
|
|
66
|
-
channel: channel.id,
|
|
67
|
-
ts: thread.ts,
|
|
68
|
-
});
|
|
69
|
-
const allMsgs = replies.messages ?? [];
|
|
70
|
-
const text = allMsgs.map(m => m.text ?? '').join('\n');
|
|
71
|
-
// Who to talk to (ALI-118): the thread starter (original poster).
|
|
72
|
-
const author = await resolveUser(allMsgs[0]?.user ?? thread.user);
|
|
73
|
-
items.push({
|
|
74
|
-
source_url: `https://slack.com/archives/${channel.id}/p${thread.ts.replace('.', '')}`,
|
|
75
|
-
platform: 'slack',
|
|
76
|
-
raw_text: `[#${channel.name}] Thread:\n${text}`,
|
|
77
|
-
title: (thread.text ?? `Thread in #${channel.name}`).slice(0, 80),
|
|
78
|
-
...(author ? { author } : {}),
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
catch { /* skip individual thread errors */ }
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
catch { /* skip channels that are inaccessible */ }
|
|
85
|
-
}
|
|
86
|
-
return items;
|
|
4
|
+
return new SlackFetcher().fetch(opts);
|
|
87
5
|
}
|
|
88
6
|
//# sourceMappingURL=slack.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slack.js","sourceRoot":"","sources":["../../../src/lib/fetchers/slack.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"slack.js","sourceRoot":"","sources":["../../../src/lib/fetchers/slack.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAG5D,6EAA6E;AAC7E,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAA0D;IAC9F,OAAO,IAAI,YAAY,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AACxC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"teams.d.ts","sourceRoot":"","sources":["../../../src/lib/fetchers/teams.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"teams.d.ts","sourceRoot":"","sources":["../../../src/lib/fetchers/teams.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAEhE,6EAA6E;AAC7E,wBAAsB,eAAe,CAAC,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAE5G"}
|
|
@@ -1,65 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
}
|
|
4
|
-
async function graphGet(path, token) {
|
|
5
|
-
const res = await fetch(`https://graph.microsoft.com/v1.0${path}`, {
|
|
6
|
-
headers: { Authorization: `Bearer ${token}` },
|
|
7
|
-
});
|
|
8
|
-
if (!res.ok) {
|
|
9
|
-
const err = await res.json().catch(() => ({}));
|
|
10
|
-
const code = err.error?.code ?? '';
|
|
11
|
-
if (res.status === 403 || code.includes('Authorization') || code.includes('Consent')) {
|
|
12
|
-
throw new Error('Teams requires admin consent for ChannelMessage.Read.All. ' +
|
|
13
|
-
'Ask your Microsoft 365 admin to grant consent, or see: ' +
|
|
14
|
-
'https://entra.microsoft.com/#view/Microsoft_AAD_IAM/ConsentPoliciesMenuBlade');
|
|
15
|
-
}
|
|
16
|
-
throw new Error(`Microsoft Graph API error ${res.status} on ${path}: ${err.error?.message ?? 'unknown'}`);
|
|
17
|
-
}
|
|
18
|
-
return res.json();
|
|
19
|
-
}
|
|
20
|
-
function extractText(body) {
|
|
21
|
-
if (!body?.content)
|
|
22
|
-
return '';
|
|
23
|
-
return body.contentType === 'html' ? stripHtml(body.content) : body.content;
|
|
24
|
-
}
|
|
1
|
+
import { TeamsFetcher } from '@aligndottech/connector-core';
|
|
2
|
+
/** Read-only personal Teams import (canonical fetcher in connector-core). */
|
|
25
3
|
export async function fetchTeamsItems(opts) {
|
|
26
|
-
|
|
27
|
-
const teams = await graphGet('/me/joinedTeams', opts.token);
|
|
28
|
-
const items = [];
|
|
29
|
-
for (const team of teams.value) {
|
|
30
|
-
if (items.length >= limit)
|
|
31
|
-
break;
|
|
32
|
-
const channels = await graphGet(`/teams/${team.id}/channels`, opts.token);
|
|
33
|
-
for (const channel of channels.value) {
|
|
34
|
-
if (items.length >= limit)
|
|
35
|
-
break;
|
|
36
|
-
try {
|
|
37
|
-
const msgs = await graphGet(`/teams/${team.id}/channels/${channel.id}/messages?$top=10`, opts.token);
|
|
38
|
-
for (const msg of msgs.value) {
|
|
39
|
-
if (items.length >= limit)
|
|
40
|
-
break;
|
|
41
|
-
const mainText = extractText(msg.body);
|
|
42
|
-
const replyTexts = (msg.replies ?? []).map(r => extractText(r.body)).filter(Boolean);
|
|
43
|
-
const raw_text = [
|
|
44
|
-
`[${team.displayName} > #${channel.displayName}]`,
|
|
45
|
-
msg.subject ? `Subject: ${msg.subject}` : '',
|
|
46
|
-
mainText,
|
|
47
|
-
...replyTexts,
|
|
48
|
-
].filter(Boolean).join('\n');
|
|
49
|
-
const fromName = msg.from?.user?.displayName;
|
|
50
|
-
items.push({
|
|
51
|
-
source_url: msg.webUrl ?? 'https://teams.microsoft.com',
|
|
52
|
-
platform: 'teams',
|
|
53
|
-
raw_text,
|
|
54
|
-
title: (msg.subject ?? mainText).slice(0, 80) || `Message in ${team.displayName}`,
|
|
55
|
-
// Who to talk to (ALI-118): the message author.
|
|
56
|
-
...(fromName ? { author: { name: fromName } } : {}),
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
catch { /* skip inaccessible channels */ }
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return items;
|
|
4
|
+
return new TeamsFetcher().fetch(opts);
|
|
64
5
|
}
|
|
65
6
|
//# sourceMappingURL=teams.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"teams.js","sourceRoot":"","sources":["../../../src/lib/fetchers/teams.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"teams.js","sourceRoot":"","sources":["../../../src/lib/fetchers/teams.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAG5D,6EAA6E;AAC7E,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAuC;IAC3E,OAAO,IAAI,YAAY,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AACxC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zoom.d.ts","sourceRoot":"","sources":["../../../src/lib/fetchers/zoom.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"zoom.d.ts","sourceRoot":"","sources":["../../../src/lib/fetchers/zoom.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAEhE,4EAA4E;AAC5E,wBAAsB,cAAc,CAAC,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAE1H"}
|
|
@@ -1,65 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
.split('\n')
|
|
4
|
-
.filter(line => line.trim() !== '' &&
|
|
5
|
-
line.trim() !== 'WEBVTT' &&
|
|
6
|
-
!/^\d+$/.test(line.trim()) &&
|
|
7
|
-
!line.includes(' --> '))
|
|
8
|
-
.join(' ')
|
|
9
|
-
.replace(/\s+/g, ' ')
|
|
10
|
-
.trim();
|
|
11
|
-
}
|
|
12
|
-
function encodeMeetingUuid(uuid) {
|
|
13
|
-
const encoded = encodeURIComponent(uuid);
|
|
14
|
-
return uuid.includes('//') ? encodeURIComponent(encoded) : encoded;
|
|
15
|
-
}
|
|
16
|
-
async function zoomGet(path, token) {
|
|
17
|
-
const res = await fetch(`https://api.zoom.us/v2${path}`, {
|
|
18
|
-
headers: { Authorization: `Bearer ${token}` },
|
|
19
|
-
});
|
|
20
|
-
if (!res.ok) {
|
|
21
|
-
const err = await res.json().catch(() => ({}));
|
|
22
|
-
throw new Error(`Zoom API error ${res.status}: ${err.message ?? 'unknown'}`);
|
|
23
|
-
}
|
|
24
|
-
return res.json();
|
|
25
|
-
}
|
|
1
|
+
import { ZoomFetcher } from '@aligndottech/connector-core';
|
|
2
|
+
/** Read-only personal Zoom import (canonical fetcher in connector-core). */
|
|
26
3
|
export async function fetchZoomItems(opts) {
|
|
27
|
-
|
|
28
|
-
const path = opts.uuid
|
|
29
|
-
? `/meetings/${encodeMeetingUuid(opts.uuid)}/recordings`
|
|
30
|
-
: `/users/me/recordings?page_size=30`;
|
|
31
|
-
const data = await zoomGet(path, opts.token);
|
|
32
|
-
const meetings = data.meetings ?? [];
|
|
33
|
-
const items = [];
|
|
34
|
-
for (const meeting of meetings) {
|
|
35
|
-
if (items.length >= limit)
|
|
36
|
-
break;
|
|
37
|
-
const vttFile = (meeting.recording_files ?? []).find(f => f.file_type === 'TRANSCRIPT' && f.status === 'completed');
|
|
38
|
-
if (!vttFile)
|
|
39
|
-
continue;
|
|
40
|
-
try {
|
|
41
|
-
const vttRes = await fetch(`${vttFile.download_url}?access_token=${opts.token}`);
|
|
42
|
-
if (!vttRes.ok)
|
|
43
|
-
continue;
|
|
44
|
-
const vttText = await vttRes.text();
|
|
45
|
-
const transcript = parseWebVtt(vttText);
|
|
46
|
-
if (!transcript)
|
|
47
|
-
continue;
|
|
48
|
-
const date = meeting.start_time.slice(0, 10);
|
|
49
|
-
// Who to talk to (ALI-119): the meeting host.
|
|
50
|
-
const host = meeting.host_email
|
|
51
|
-
? { name: meeting.host_email.split('@')[0], email: meeting.host_email }
|
|
52
|
-
: undefined;
|
|
53
|
-
items.push({
|
|
54
|
-
source_url: `https://zoom.us/recording/${encodeMeetingUuid(meeting.uuid)}`,
|
|
55
|
-
platform: 'zoom',
|
|
56
|
-
raw_text: `[${meeting.topic} - ${date}]\n${transcript}`.slice(0, 4000),
|
|
57
|
-
title: `${meeting.topic} (${date})`.slice(0, 80),
|
|
58
|
-
...(host ? { author: host } : {}),
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
catch { /* skip recordings with inaccessible transcripts */ }
|
|
62
|
-
}
|
|
63
|
-
return items;
|
|
4
|
+
return new ZoomFetcher().fetch(opts);
|
|
64
5
|
}
|
|
65
6
|
//# sourceMappingURL=zoom.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zoom.js","sourceRoot":"","sources":["../../../src/lib/fetchers/zoom.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"zoom.js","sourceRoot":"","sources":["../../../src/lib/fetchers/zoom.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAG3D,4EAA4E;AAC5E,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAAsD;IACzF,OAAO,IAAI,WAAW,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AACvC,CAAC"}
|
|
@@ -122,20 +122,8 @@ export interface DriftItem {
|
|
|
122
122
|
drift_summary: string;
|
|
123
123
|
checked_at: string;
|
|
124
124
|
}
|
|
125
|
-
|
|
126
|
-
export
|
|
127
|
-
name: string;
|
|
128
|
-
handle?: string;
|
|
129
|
-
email?: string;
|
|
130
|
-
url?: string;
|
|
131
|
-
}
|
|
132
|
-
export interface BatchIngestItem {
|
|
133
|
-
source_url: string;
|
|
134
|
-
platform: string;
|
|
135
|
-
raw_text: string;
|
|
136
|
-
title?: string;
|
|
137
|
-
author?: DecisionAuthor;
|
|
138
|
-
}
|
|
125
|
+
import type { FetcherItem as BatchIngestItem, DecisionAuthor } from '@aligndottech/connector-core';
|
|
126
|
+
export type { DecisionAuthor, BatchIngestItem };
|
|
139
127
|
export interface BatchIngestResult {
|
|
140
128
|
snapshots: Array<{
|
|
141
129
|
id: string;
|
|
@@ -229,5 +217,4 @@ declare function buildHttpGatewayClient(env: EnvironmentConfig): {
|
|
|
229
217
|
getStreamUrl(jobId: string): string;
|
|
230
218
|
};
|
|
231
219
|
export declare function createGatewayClient(env: EnvironmentConfig): ReturnType<typeof buildHttpGatewayClient>;
|
|
232
|
-
export {};
|
|
233
220
|
//# sourceMappingURL=gateway-client.d.ts.map
|