@lobu/cli 6.1.1 → 7.0.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/_lib/apply/apply-cmd.d.ts +36 -0
- package/dist/commands/_lib/apply/apply-cmd.d.ts.map +1 -1
- package/dist/commands/_lib/apply/apply-cmd.js +548 -40
- package/dist/commands/_lib/apply/apply-cmd.js.map +1 -1
- package/dist/commands/_lib/apply/client.d.ts +179 -0
- package/dist/commands/_lib/apply/client.d.ts.map +1 -1
- package/dist/commands/_lib/apply/client.js +308 -28
- package/dist/commands/_lib/apply/client.js.map +1 -1
- package/dist/commands/_lib/apply/desired-state.d.ts +134 -3
- package/dist/commands/_lib/apply/desired-state.d.ts.map +1 -1
- package/dist/commands/_lib/apply/desired-state.js +700 -86
- package/dist/commands/_lib/apply/desired-state.js.map +1 -1
- package/dist/commands/_lib/apply/diff.d.ts +61 -3
- package/dist/commands/_lib/apply/diff.d.ts.map +1 -1
- package/dist/commands/_lib/apply/diff.js +382 -92
- package/dist/commands/_lib/apply/diff.js.map +1 -1
- package/dist/commands/_lib/apply/prompt.d.ts +6 -0
- package/dist/commands/_lib/apply/prompt.d.ts.map +1 -1
- package/dist/commands/_lib/apply/prompt.js +16 -0
- package/dist/commands/_lib/apply/prompt.js.map +1 -1
- package/dist/commands/_lib/apply/render.d.ts +9 -0
- package/dist/commands/_lib/apply/render.d.ts.map +1 -1
- package/dist/commands/_lib/apply/render.js +80 -3
- package/dist/commands/_lib/apply/render.js.map +1 -1
- package/dist/commands/chat.d.ts.map +1 -1
- package/dist/commands/chat.js +9 -2
- package/dist/commands/chat.js.map +1 -1
- package/dist/commands/dev.d.ts +8 -0
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +118 -5
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/eval.d.ts.map +1 -1
- package/dist/commands/eval.js +16 -5
- package/dist/commands/eval.js.map +1 -1
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +24 -0
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/memory/_lib/schema.d.ts +28 -1
- package/dist/commands/memory/_lib/schema.d.ts.map +1 -1
- package/dist/commands/memory/_lib/schema.js +120 -4
- package/dist/commands/memory/_lib/schema.js.map +1 -1
- package/dist/commands/memory/_lib/seed-cmd.d.ts.map +1 -1
- package/dist/commands/memory/_lib/seed-cmd.js +41 -18
- package/dist/commands/memory/_lib/seed-cmd.js.map +1 -1
- package/dist/commands/org.d.ts +4 -0
- package/dist/commands/org.d.ts.map +1 -1
- package/dist/commands/org.js +10 -0
- package/dist/commands/org.js.map +1 -1
- package/dist/commands/token.d.ts +9 -0
- package/dist/commands/token.d.ts.map +1 -1
- package/dist/commands/token.js +54 -0
- package/dist/commands/token.js.map +1 -1
- package/dist/connectors/README.md +2 -2
- package/dist/connectors/apple_health.ts +138 -0
- package/dist/connectors/apple_screen_time.ts +82 -0
- package/dist/connectors/browser-scraper-utils.ts +35 -3
- package/dist/connectors/capterra.ts +5 -1
- package/dist/connectors/g2.ts +5 -1
- package/dist/connectors/github.ts +15 -38
- package/dist/connectors/glassdoor.ts +5 -1
- package/dist/connectors/google_calendar.ts +14 -4
- package/dist/connectors/google_gmail.ts +6 -3
- package/dist/connectors/google_play.ts +10 -3
- package/dist/connectors/index.ts +5 -0
- package/dist/connectors/linkedin.ts +32 -9
- package/dist/connectors/local_directory.ts +91 -0
- package/dist/connectors/revolut.ts +572 -0
- package/dist/connectors/trustpilot.ts +5 -1
- package/dist/connectors/website.ts +1 -1
- package/dist/connectors/whatsapp.ts +9 -1
- package/dist/connectors/whatsapp_local.ts +125 -0
- package/dist/connectors/x.ts +17 -7
- package/dist/db/migrations/20260510220000_connector_required_capability.sql +47 -0
- package/dist/db/migrations/20260512000000_device_worker_connection_binding.sql +113 -0
- package/dist/db/migrations/20260512131703_connections_slug.sql +131 -0
- package/dist/db/migrations/20260513000000_chat_user_identities.sql +24 -0
- package/dist/db/migrations/20260513120000_auth_profiles_device_binding.sql +50 -0
- package/dist/db/migrations/20260513150000_auth_profiles_cdp_url.sql +43 -0
- package/dist/db/migrations/20260513200000_notifications_as_events.sql +86 -0
- package/dist/db/migrations/20260514000000_scheduled_jobs.sql +97 -0
- package/dist/db/migrations/20260514120000_auth_profiles_connector_key_nullable.sql +42 -0
- package/dist/eval/types.d.ts +2 -0
- package/dist/eval/types.d.ts.map +1 -1
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +68 -114
- package/dist/index.js.map +1 -1
- package/dist/internal/gateway-url.d.ts +14 -0
- package/dist/internal/gateway-url.d.ts.map +1 -1
- package/dist/internal/gateway-url.js +19 -0
- package/dist/internal/gateway-url.js.map +1 -1
- package/dist/internal/index.d.ts +1 -1
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +1 -1
- package/dist/internal/index.js.map +1 -1
- package/dist/server.bundle.mjs +32494 -30475
- package/dist/start-local.bundle.mjs +10840 -7912
- package/dist/templates/TESTING.md.tmpl +9 -9
- package/package.json +6 -6
|
@@ -53,6 +53,14 @@ interface RepoRef {
|
|
|
53
53
|
repo: string;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
interface GitHubMutationResponse {
|
|
57
|
+
id: number;
|
|
58
|
+
number: number;
|
|
59
|
+
html_url: string;
|
|
60
|
+
state: string;
|
|
61
|
+
draft?: boolean;
|
|
62
|
+
}
|
|
63
|
+
|
|
56
64
|
interface GitHubRepositoryLike {
|
|
57
65
|
id?: number;
|
|
58
66
|
full_name?: string;
|
|
@@ -168,13 +176,6 @@ function toIsoOrUndefined(value: unknown): string | undefined {
|
|
|
168
176
|
return Number.isNaN(parsed.getTime()) ? undefined : parsed.toISOString();
|
|
169
177
|
}
|
|
170
178
|
|
|
171
|
-
function stripMarkdown(code: string): string {
|
|
172
|
-
return code
|
|
173
|
-
.replace(/```[a-zA-Z]*\n?/g, '')
|
|
174
|
-
.replace(/```/g, '')
|
|
175
|
-
.trim();
|
|
176
|
-
}
|
|
177
|
-
|
|
178
179
|
const REPO_PROPS = {
|
|
179
180
|
repo_owner: { type: 'string', minLength: 1, description: 'Repository owner' },
|
|
180
181
|
repo_name: { type: 'string', minLength: 1, description: 'Repository name' },
|
|
@@ -569,7 +570,7 @@ export default class GitHubConnector extends ConnectorRuntime {
|
|
|
569
570
|
};
|
|
570
571
|
|
|
571
572
|
async sync(ctx: SyncContext): Promise<SyncResult> {
|
|
572
|
-
const config =
|
|
573
|
+
const config = ctx.config as GitHubConfig;
|
|
573
574
|
const repo = this.resolveRepo(config, {});
|
|
574
575
|
const token = this.resolveToken(ctx.credentials?.accessToken, config);
|
|
575
576
|
const contentType = (ctx.feedKey ?? 'issues') as GitHubContentType;
|
|
@@ -611,7 +612,7 @@ export default class GitHubConnector extends ConnectorRuntime {
|
|
|
611
612
|
|
|
612
613
|
async execute(ctx: ActionContext): Promise<ActionResult> {
|
|
613
614
|
try {
|
|
614
|
-
const config =
|
|
615
|
+
const config = ctx.config as GitHubConfig;
|
|
615
616
|
const repo = this.resolveRepo(config, ctx.input);
|
|
616
617
|
const token = this.resolveToken(ctx.credentials?.accessToken, config);
|
|
617
618
|
|
|
@@ -643,10 +644,6 @@ export default class GitHubConnector extends ConnectorRuntime {
|
|
|
643
644
|
}
|
|
644
645
|
}
|
|
645
646
|
|
|
646
|
-
private parseConfig(raw: Record<string, unknown>): GitHubConfig {
|
|
647
|
-
return raw as GitHubConfig;
|
|
648
|
-
}
|
|
649
|
-
|
|
650
647
|
private resolveRepo(config: GitHubConfig, input: Record<string, unknown>): RepoRef {
|
|
651
648
|
const owner = asString(input.repo_owner) ?? config.repo_owner;
|
|
652
649
|
const repo = asString(input.repo_name) ?? config.repo_name;
|
|
@@ -686,7 +683,7 @@ export default class GitHubConnector extends ConnectorRuntime {
|
|
|
686
683
|
|
|
687
684
|
private async syncContent(params: {
|
|
688
685
|
repo: RepoRef;
|
|
689
|
-
contentType: GitHubContentType
|
|
686
|
+
contentType: Exclude<GitHubContentType, 'stargazers'>;
|
|
690
687
|
sinceIso: string;
|
|
691
688
|
labelsFilter: string[];
|
|
692
689
|
token: string | null;
|
|
@@ -705,10 +702,6 @@ export default class GitHubConnector extends ConnectorRuntime {
|
|
|
705
702
|
return await this.syncDiscussions(repo, sinceIso, token);
|
|
706
703
|
case 'discussion_comments':
|
|
707
704
|
return await this.syncDiscussionComments(repo, sinceIso, token);
|
|
708
|
-
case 'stargazers':
|
|
709
|
-
return [];
|
|
710
|
-
default:
|
|
711
|
-
return [];
|
|
712
705
|
}
|
|
713
706
|
}
|
|
714
707
|
|
|
@@ -1327,12 +1320,7 @@ export default class GitHubConnector extends ConnectorRuntime {
|
|
|
1327
1320
|
? input.assignees.filter((value): value is string => typeof value === 'string')
|
|
1328
1321
|
: undefined;
|
|
1329
1322
|
|
|
1330
|
-
const issue = await this.requestJson<{
|
|
1331
|
-
id: number;
|
|
1332
|
-
number: number;
|
|
1333
|
-
html_url: string;
|
|
1334
|
-
state: string;
|
|
1335
|
-
}>({
|
|
1323
|
+
const issue = await this.requestJson<GitHubMutationResponse>({
|
|
1336
1324
|
method: 'POST',
|
|
1337
1325
|
url: `https://api.github.com/repos/${repo.owner}/${repo.repo}/issues`,
|
|
1338
1326
|
token,
|
|
@@ -1394,12 +1382,7 @@ export default class GitHubConnector extends ConnectorRuntime {
|
|
|
1394
1382
|
const issueNumber = toInt(input.issue_number, 0);
|
|
1395
1383
|
if (!issueNumber) return { success: false, error: 'issue_number is required' };
|
|
1396
1384
|
|
|
1397
|
-
const issue = await this.requestJson<{
|
|
1398
|
-
id: number;
|
|
1399
|
-
number: number;
|
|
1400
|
-
html_url: string;
|
|
1401
|
-
state: string;
|
|
1402
|
-
}>({
|
|
1385
|
+
const issue = await this.requestJson<GitHubMutationResponse>({
|
|
1403
1386
|
method: 'PATCH',
|
|
1404
1387
|
url: `https://api.github.com/repos/${repo.owner}/${repo.repo}/issues/${issueNumber}`,
|
|
1405
1388
|
token,
|
|
@@ -1432,13 +1415,7 @@ export default class GitHubConnector extends ConnectorRuntime {
|
|
|
1432
1415
|
const body = asString(input.body);
|
|
1433
1416
|
const draft = typeof input.draft === 'boolean' ? input.draft : undefined;
|
|
1434
1417
|
|
|
1435
|
-
const pr = await this.requestJson<{
|
|
1436
|
-
id: number;
|
|
1437
|
-
number: number;
|
|
1438
|
-
html_url: string;
|
|
1439
|
-
state: string;
|
|
1440
|
-
draft?: boolean;
|
|
1441
|
-
}>({
|
|
1418
|
+
const pr = await this.requestJson<GitHubMutationResponse>({
|
|
1442
1419
|
method: 'POST',
|
|
1443
1420
|
url: `https://api.github.com/repos/${repo.owner}/${repo.repo}/pulls`,
|
|
1444
1421
|
token,
|
|
@@ -1489,7 +1466,7 @@ export default class GitHubConnector extends ConnectorRuntime {
|
|
|
1489
1466
|
? mergeMethod
|
|
1490
1467
|
: undefined,
|
|
1491
1468
|
commit_title: commitTitle,
|
|
1492
|
-
commit_message: commitMessage
|
|
1469
|
+
commit_message: commitMessage,
|
|
1493
1470
|
},
|
|
1494
1471
|
});
|
|
1495
1472
|
|
|
@@ -16,6 +16,8 @@ import {
|
|
|
16
16
|
type SyncResult,
|
|
17
17
|
} from '@lobu/connector-sdk';
|
|
18
18
|
import {
|
|
19
|
+
getBrowserCdpUrl,
|
|
20
|
+
getBrowserUserDataDir,
|
|
19
21
|
handleCookieConsent,
|
|
20
22
|
openStealthBrowser,
|
|
21
23
|
validateUrlDomain,
|
|
@@ -158,7 +160,9 @@ export default class GlassdoorConnector extends ConnectorRuntime {
|
|
|
158
160
|
: `https://www.glassdoor.com/Reviews/${company_name}-reviews-SRCH_KE0.htm`;
|
|
159
161
|
validateUrlDomain(baseUrl, 'glassdoor.com');
|
|
160
162
|
|
|
161
|
-
const
|
|
163
|
+
const userDataDir = getBrowserUserDataDir(ctx.sessionState);
|
|
164
|
+
const cdpUrl = getBrowserCdpUrl(ctx.sessionState) ?? 'auto';
|
|
165
|
+
const session = await openStealthBrowser({ cdpUrl, userDataDir });
|
|
162
166
|
|
|
163
167
|
return withBrowserErrorCapture(session, 'glassdoor-sync', async (page) => {
|
|
164
168
|
// Configure viewport and user-agent to mimic a real browser
|
|
@@ -256,8 +256,11 @@ export default class GoogleCalendarConnector extends ConnectorRuntime {
|
|
|
256
256
|
let nextSyncToken: string | undefined;
|
|
257
257
|
|
|
258
258
|
while (true) {
|
|
259
|
+
// Always request a full page — `maxResults` is a soft cap on *stored*
|
|
260
|
+
// events, not a reason to shrink the request size (shrinking to 1 once the
|
|
261
|
+
// cap is hit would crawl a busy calendar one event per round-trip).
|
|
259
262
|
const params = new URLSearchParams({
|
|
260
|
-
maxResults:
|
|
263
|
+
maxResults: '250',
|
|
261
264
|
orderBy: 'startTime',
|
|
262
265
|
singleEvents: 'true',
|
|
263
266
|
timeMin: timeMin.toISOString(),
|
|
@@ -280,6 +283,7 @@ export default class GoogleCalendarConnector extends ConnectorRuntime {
|
|
|
280
283
|
|
|
281
284
|
if (data.items) {
|
|
282
285
|
for (const calEvent of data.items) {
|
|
286
|
+
if (events.length >= maxResults) break;
|
|
283
287
|
const envelope = this.calendarEventToEnvelope(calEvent);
|
|
284
288
|
if (envelope) events.push(envelope);
|
|
285
289
|
}
|
|
@@ -287,7 +291,12 @@ export default class GoogleCalendarConnector extends ConnectorRuntime {
|
|
|
287
291
|
|
|
288
292
|
nextSyncToken = data.nextSyncToken;
|
|
289
293
|
pageToken = data.nextPageToken;
|
|
290
|
-
|
|
294
|
+
// Google only returns nextSyncToken on the LAST page (no nextPageToken).
|
|
295
|
+
// Must keep paginating until pageToken is exhausted, otherwise the sync
|
|
296
|
+
// token is never obtained and every subsequent sync re-runs the full
|
|
297
|
+
// window from scratch — so we keep paging past `maxResults`, just stop
|
|
298
|
+
// appending events once the cap is reached.
|
|
299
|
+
if (!pageToken) break;
|
|
291
300
|
}
|
|
292
301
|
|
|
293
302
|
return this.buildResult(events, nextSyncToken, events.length);
|
|
@@ -343,7 +352,7 @@ export default class GoogleCalendarConnector extends ConnectorRuntime {
|
|
|
343
352
|
|
|
344
353
|
while (true) {
|
|
345
354
|
const params = new URLSearchParams({
|
|
346
|
-
maxResults: String(Math.min(250, maxResults - events.length)),
|
|
355
|
+
maxResults: String(Math.max(1, Math.min(250, maxResults - events.length))),
|
|
347
356
|
syncToken,
|
|
348
357
|
});
|
|
349
358
|
if (pageToken) {
|
|
@@ -375,7 +384,8 @@ export default class GoogleCalendarConnector extends ConnectorRuntime {
|
|
|
375
384
|
|
|
376
385
|
nextSyncToken = data.nextSyncToken;
|
|
377
386
|
pageToken = data.nextPageToken;
|
|
378
|
-
|
|
387
|
+
// Paginate until exhausted so we capture the trailing nextSyncToken.
|
|
388
|
+
if (!pageToken) break;
|
|
379
389
|
}
|
|
380
390
|
|
|
381
391
|
return { events, nextSyncToken };
|
|
@@ -135,7 +135,7 @@ export default class GmailConnector extends ConnectorRuntime {
|
|
|
135
135
|
},
|
|
136
136
|
entityLinks: [
|
|
137
137
|
{
|
|
138
|
-
entityType: '
|
|
138
|
+
entityType: 'person',
|
|
139
139
|
autoCreate: true,
|
|
140
140
|
titlePath: 'metadata.from_name',
|
|
141
141
|
identities: [{ namespace: IDENTITY.EMAIL, eventPath: 'metadata.from_email' }],
|
|
@@ -274,8 +274,11 @@ export default class GmailConnector extends ConnectorRuntime {
|
|
|
274
274
|
return d;
|
|
275
275
|
})();
|
|
276
276
|
|
|
277
|
-
|
|
278
|
-
|
|
277
|
+
// Gmail's `after:` accepts a Unix timestamp (epoch seconds) for second-level
|
|
278
|
+
// precision. Using `YYYY/MM/DD` (day granularity, host timezone) meant every
|
|
279
|
+
// sync within the same day re-fetched the whole day's threads as duplicates.
|
|
280
|
+
const afterEpochSeconds = Math.floor(afterDate.getTime() / 1000);
|
|
281
|
+
const query = `after:${afterEpochSeconds} label:${label}`;
|
|
279
282
|
|
|
280
283
|
const events: EventEnvelope[] = [];
|
|
281
284
|
let pageToken: string | undefined;
|
|
@@ -73,9 +73,16 @@ interface RawReview {
|
|
|
73
73
|
*/
|
|
74
74
|
function parseDate(dateArray: unknown): string | null {
|
|
75
75
|
if (!Array.isArray(dateArray)) return null;
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
// Compute numerically: seconds*1000 + millis. The previous string-concat
|
|
77
|
+
// approach (`${seconds}${millis}`) only worked when millis was a 3-digit
|
|
78
|
+
// zero-padded string; Google sends a plain integer, so e.g. `[s, 5]` produced
|
|
79
|
+
// a date in 1970 and `[s, 50]` a date in year ~7340.
|
|
80
|
+
const seconds = Number(dateArray[0]);
|
|
81
|
+
const millis = Number(dateArray[1] ?? 0);
|
|
82
|
+
if (!Number.isFinite(seconds) || !Number.isFinite(millis)) return null;
|
|
83
|
+
const d = new Date(seconds * 1000 + millis);
|
|
84
|
+
if (Number.isNaN(d.getTime())) return null;
|
|
85
|
+
return d.toJSON();
|
|
79
86
|
}
|
|
80
87
|
|
|
81
88
|
/**
|
package/dist/connectors/index.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
export * from './apple_health.ts';
|
|
2
|
+
export * from './apple_screen_time.ts';
|
|
3
|
+
export * from './local_directory.ts';
|
|
1
4
|
export * from './browser-scraper-utils.ts';
|
|
2
5
|
export * from './capterra.ts';
|
|
3
6
|
export * from './g2.ts';
|
|
@@ -14,10 +17,12 @@ export * from './linkedin.ts';
|
|
|
14
17
|
export * from './microsoft_outlook.ts';
|
|
15
18
|
export * from './producthunt.ts';
|
|
16
19
|
export * from './reddit.ts';
|
|
20
|
+
export * from './revolut.ts';
|
|
17
21
|
export * from './rss.ts';
|
|
18
22
|
export * from './spotify.ts';
|
|
19
23
|
export * from './trustpilot.ts';
|
|
20
24
|
export * from './website.ts';
|
|
21
25
|
export * from './whatsapp.ts';
|
|
26
|
+
export * from './whatsapp_local.ts';
|
|
22
27
|
export * from './x.ts';
|
|
23
28
|
export * from './youtube.ts';
|
|
@@ -19,7 +19,12 @@ import {
|
|
|
19
19
|
type SyncContext,
|
|
20
20
|
type SyncResult,
|
|
21
21
|
} from '@lobu/connector-sdk';
|
|
22
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
getBrowserCdpUrl,
|
|
24
|
+
getBrowserCookies,
|
|
25
|
+
getBrowserUserDataDir,
|
|
26
|
+
validateCookieNotExpired,
|
|
27
|
+
} from './browser-scraper-utils';
|
|
23
28
|
|
|
24
29
|
// ── Types ──────────────────────────────────────────────────────
|
|
25
30
|
|
|
@@ -316,23 +321,37 @@ export default class LinkedInConnector extends ConnectorRuntime {
|
|
|
316
321
|
// Normalize URL - remove trailing slash
|
|
317
322
|
const baseUrl = companyUrl.replace(/\/$/, '');
|
|
318
323
|
|
|
319
|
-
const
|
|
320
|
-
|
|
324
|
+
const userDataDir = getBrowserUserDataDir(ctx.sessionState);
|
|
325
|
+
const cdpUrlFromSession = getBrowserCdpUrl(ctx.sessionState);
|
|
326
|
+
const cdpUrl = cdpUrlFromSession ?? 'auto';
|
|
327
|
+
// No need to require cookies when the device tells us to attach directly
|
|
328
|
+
// (managed --user-data-dir on disk, or an explicit CDP endpoint pointed
|
|
329
|
+
// at the user's running Chrome). The cookie cascade is only the fallback
|
|
330
|
+
// for the cloud/auto path.
|
|
331
|
+
const skipServerCookies = !!userDataDir || !!cdpUrlFromSession;
|
|
332
|
+
const cookies = skipServerCookies
|
|
333
|
+
? []
|
|
334
|
+
: getBrowserCookies(ctx.checkpoint as any, ctx.sessionState as any, 'linkedin');
|
|
335
|
+
if (!skipServerCookies) {
|
|
336
|
+
validateCookieNotExpired(cookies, 'li_at', 'linkedin');
|
|
337
|
+
}
|
|
321
338
|
|
|
322
339
|
const maxScrolls = (config.max_scrolls as number) ?? (feedKey === 'jobs' ? 3 : 5);
|
|
323
340
|
|
|
324
341
|
if (feedKey === 'jobs') {
|
|
325
|
-
return this.syncJobs(baseUrl, cookies, maxScrolls, checkpoint);
|
|
342
|
+
return this.syncJobs(baseUrl, cookies, maxScrolls, checkpoint, userDataDir, cdpUrl);
|
|
326
343
|
}
|
|
327
344
|
|
|
328
|
-
return this.syncUpdates(baseUrl, cookies, maxScrolls, checkpoint);
|
|
345
|
+
return this.syncUpdates(baseUrl, cookies, maxScrolls, checkpoint, userDataDir, cdpUrl);
|
|
329
346
|
}
|
|
330
347
|
|
|
331
348
|
private async syncUpdates(
|
|
332
349
|
baseUrl: string,
|
|
333
350
|
cookies: any[],
|
|
334
351
|
maxScrolls: number,
|
|
335
|
-
checkpoint: LinkedInCheckpoint
|
|
352
|
+
checkpoint: LinkedInCheckpoint,
|
|
353
|
+
userDataDir: string | undefined,
|
|
354
|
+
cdpUrl: string | 'auto'
|
|
336
355
|
): Promise<SyncResult> {
|
|
337
356
|
const postsUrl = `${baseUrl}/posts/`;
|
|
338
357
|
|
|
@@ -350,8 +369,9 @@ export default class LinkedInConnector extends ConnectorRuntime {
|
|
|
350
369
|
navigationTimeoutMs: 20000,
|
|
351
370
|
},
|
|
352
371
|
url: postsUrl,
|
|
353
|
-
cdpUrl
|
|
372
|
+
cdpUrl,
|
|
354
373
|
cookies,
|
|
374
|
+
userDataDir,
|
|
355
375
|
parseResponse: parseCompanyUpdates,
|
|
356
376
|
checkAuth: async (page) => {
|
|
357
377
|
const url = page.url();
|
|
@@ -401,7 +421,9 @@ export default class LinkedInConnector extends ConnectorRuntime {
|
|
|
401
421
|
baseUrl: string,
|
|
402
422
|
cookies: any[],
|
|
403
423
|
maxScrolls: number,
|
|
404
|
-
checkpoint: LinkedInCheckpoint
|
|
424
|
+
checkpoint: LinkedInCheckpoint,
|
|
425
|
+
userDataDir: string | undefined,
|
|
426
|
+
cdpUrl: string | 'auto'
|
|
405
427
|
): Promise<SyncResult> {
|
|
406
428
|
const jobsUrl = `${baseUrl}/jobs/`;
|
|
407
429
|
|
|
@@ -420,8 +442,9 @@ export default class LinkedInConnector extends ConnectorRuntime {
|
|
|
420
442
|
navigationTimeoutMs: 20000,
|
|
421
443
|
},
|
|
422
444
|
url: jobsUrl,
|
|
423
|
-
cdpUrl
|
|
445
|
+
cdpUrl,
|
|
424
446
|
cookies,
|
|
447
|
+
userDataDir,
|
|
425
448
|
parseResponse: parseJobListings,
|
|
426
449
|
checkAuth: async (page) => {
|
|
427
450
|
const url = page.url();
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local Directory Connector (V1 runtime) — Lobu for Mac only.
|
|
3
|
+
*
|
|
4
|
+
* Syncs text files (txt/md/json/csv/html) from a local folder on the user's
|
|
5
|
+
* Mac via Lobu for Mac. The app advertises the `local_directory`
|
|
6
|
+
* capability on /api/workers/poll, reads the folder, and streams file events
|
|
7
|
+
* back through the standard worker protocol.
|
|
8
|
+
*
|
|
9
|
+
* The sync() / execute() stubs here throw immediately if a server-side worker
|
|
10
|
+
* somehow bypassed the capability gate — same pattern as apple_screen_time.ts.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
type ActionResult,
|
|
15
|
+
type ConnectorDefinition,
|
|
16
|
+
ConnectorRuntime,
|
|
17
|
+
type SyncContext,
|
|
18
|
+
type SyncResult,
|
|
19
|
+
} from '@lobu/connector-sdk';
|
|
20
|
+
|
|
21
|
+
const BRIDGE_ONLY_MESSAGE =
|
|
22
|
+
'local.directory runs only on a worker advertising capability "local_directory" (Lobu for Mac). ' +
|
|
23
|
+
'This run was claimed by a worker without that capability — check connector_definitions.required_capability and the poll-time capability filter.';
|
|
24
|
+
|
|
25
|
+
export default class LocalDirectoryConnector extends ConnectorRuntime {
|
|
26
|
+
readonly definition: ConnectorDefinition = {
|
|
27
|
+
key: 'local.directory',
|
|
28
|
+
name: 'Local Folder',
|
|
29
|
+
description:
|
|
30
|
+
'Sync text files (txt/md/json/csv/html) from a folder on your Mac via Lobu for Mac.',
|
|
31
|
+
version: '0.1.0',
|
|
32
|
+
faviconDomain: 'apple.com',
|
|
33
|
+
requiredCapability: 'local_directory',
|
|
34
|
+
runtime: { platforms: ['macos'] },
|
|
35
|
+
authSchema: { methods: [{ type: 'none' }] },
|
|
36
|
+
feeds: {
|
|
37
|
+
files: {
|
|
38
|
+
key: 'files',
|
|
39
|
+
name: 'Files',
|
|
40
|
+
description: 'Text files from one local folder on the user\'s Mac. One feed per folder — folder_id is an opaque stable id minted by the Mac app (the security-scoped bookmark is held device-side; the server never sees the absolute path).',
|
|
41
|
+
userManaged: true,
|
|
42
|
+
configSchema: {
|
|
43
|
+
type: 'object',
|
|
44
|
+
required: ['folder_id', 'display_name'],
|
|
45
|
+
properties: {
|
|
46
|
+
folder_id: {
|
|
47
|
+
type: 'string',
|
|
48
|
+
minLength: 8,
|
|
49
|
+
maxLength: 64,
|
|
50
|
+
description: 'Opaque stable id (UUID) minted on the Mac. Maps to a security-scoped bookmark stored locally on the device.',
|
|
51
|
+
},
|
|
52
|
+
display_name: {
|
|
53
|
+
type: 'string',
|
|
54
|
+
minLength: 1,
|
|
55
|
+
maxLength: 200,
|
|
56
|
+
description: 'Folder name shown in the UI (e.g., "Documents"). Not used to locate the folder — the device resolves folder_id to its bookmark.',
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
eventKinds: {
|
|
61
|
+
file_document: {
|
|
62
|
+
description: 'A text file from a configured local folder.',
|
|
63
|
+
metadataSchema: {
|
|
64
|
+
type: 'object',
|
|
65
|
+
// No absolute filesystem path — the bridge sends the folder's
|
|
66
|
+
// display name and the file name, which is enough context
|
|
67
|
+
// without leaking the user's home directory / disk layout.
|
|
68
|
+
required: ['source', 'folder', 'name'],
|
|
69
|
+
properties: {
|
|
70
|
+
source: { type: 'string', const: 'local_directory' },
|
|
71
|
+
folder: { type: 'string', description: 'Display name of the local folder.' },
|
|
72
|
+
name: { type: 'string', description: 'File name.' },
|
|
73
|
+
ext: { type: 'string' },
|
|
74
|
+
size_bytes: { type: 'number' },
|
|
75
|
+
modified_at: { type: 'string' },
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
async sync(_ctx: SyncContext): Promise<SyncResult> {
|
|
85
|
+
throw new Error(BRIDGE_ONLY_MESSAGE);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async execute(): Promise<ActionResult> {
|
|
89
|
+
throw new Error(BRIDGE_ONLY_MESSAGE);
|
|
90
|
+
}
|
|
91
|
+
}
|