Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
a16c054
feat(landing): generate per-use-case code snippets from examples/
buremba May 19, 2026
38e110b
feat(landing): CodeBlock component with TOML/YAML/TS hand-rolled high…
buremba May 19, 2026
f36b86f
feat(examples): add custom connectors for 8 dev-focused example agents
buremba May 19, 2026
6a4c2a0
feat(landing): rebuild home page around dev primitives + use-case pivot
buremba May 19, 2026
8f553a9
refactor(landing): flatten composition, lowercase mono labels, hairli…
buremba May 19, 2026
f074f3b
feat(landing): force light theme on home page
buremba May 19, 2026
e661f98
chore(landing): drop dead landing components after dev-focus rewrite
buremba May 19, 2026
2e51bbc
feat(landing): slim per-use-case snippets so each block fits without …
buremba May 19, 2026
940cf81
refactor(examples): slim 8 example connectors to fit landing without …
buremba May 19, 2026
d6d825c
chore(landing): regen snippet manifest after biome reformat of connec…
buremba May 19, 2026
8e5e6bc
feat(skill): add first-time-setup onboarding to lobu skill
buremba May 19, 2026
484024f
feat(landing): tighten snippet budgets — toml 11, memory 10, watcher …
buremba May 19, 2026
f54b7e4
fix(examples): pin connector files at 32-34 lines via biome-ignore-al…
buremba May 19, 2026
3a62d1e
fix(landing): drop period at end of hero headline
buremba May 19, 2026
558d7b2
feat(landing): per-section terminal panels driven by use-case data
buremba May 19, 2026
dfa0591
feat(landing): swap hero React terminal for vendored asciinema-player
buremba May 19, 2026
d31b647
feat(landing): shorten SETUP_PROMPT to delegate to the lobu skill
buremba May 19, 2026
df754fc
chore(landing): pick up biome reformat of generator + diagram + code …
buremba May 19, 2026
7431d87
feat(landing): copy cleanup — drop AI cadence on hero, sections, cards
buremba May 19, 2026
303c8d8
fix(landing): respect OS theme on home page + drop hero headline period
buremba May 19, 2026
71087d7
feat(landing): replace terminal result panels with real app-shell tables
buremba May 19, 2026
0e8a5ac
feat(landing): use-case grid — rework to 3 technical shapes + real ex…
buremba May 19, 2026
fd61cea
revert(landing): remove AppShellPanels result panels from round 6
buremba May 19, 2026
b1ea643
feat(landing): add Skills section between Watchers and Agents
buremba May 19, 2026
68c5c33
feat(landing): swap Skills snippet to deliveroo-order SKILL.md
buremba May 19, 2026
f4a48f3
feat(landing): drop the global use-case pivot
buremba May 19, 2026
2444a60
feat(landing): regen snippet manifest as flat pinned set + examples list
buremba May 19, 2026
7a2e869
feat(landing): rewire sections to pinned-snippet manifest + add examp…
buremba May 19, 2026
16490a6
feat(landing): replace UseCaseGrid with BrowseExamplesSection (auto-l…
buremba May 19, 2026
9320f5e
feat(landing): record real asciicast of lobu init scaffold
buremba May 19, 2026
e022428
feat(landing): swap hero cast to interactive lobu init recording
buremba May 19, 2026
1af120d
feat(landing): expand hero cast — slack preview YES, add entity, vali…
buremba May 19, 2026
72edda7
feat(landing): add claude-code TUI cast alongside lobu init cast
buremba May 19, 2026
bc670ba
feat(landing): hero cast toggle — switch between cli + agent recordings
buremba May 19, 2026
756d659
feat(landing): rewrite ArchitectureDiagram as inputs/graph/agents flow
buremba May 19, 2026
c4740d6
fix(landing): mobile overflow + drop leftover RunAnywhere card shadow
buremba May 19, 2026
5f10ef0
feat(landing): move Skills snippet to gen-landing-snippets.ts
buremba May 19, 2026
7e11f7e
feat(landing): rework architecture diagram — proper SaaS flow
buremba May 19, 2026
13c68ec
fix(landing): rename architecture columns to connectors/memory/agents
buremba May 19, 2026
670d98b
feat(landing): add Skills SubBlock to Agents column in architecture d…
buremba May 19, 2026
4308594
feat(landing): re-record agent cast — full end-to-end scaffold
buremba May 19, 2026
1b34205
feat(landing): copy sweep — terminology + concision pass
buremba May 19, 2026
7bcfbfd
fix(landing): point Vite envDir at repo root so PostHog key loads
buremba May 19, 2026
45fce74
feat(landing): animated fan-in/fan-out architecture diagram
buremba May 19, 2026
72c0ba2
feat(landing): layered architecture diagram with stream pulse
buremba May 19, 2026
9e1d7db
feat(landing): pin connector snippet to leadership/linear-cycles
buremba May 19, 2026
1712b0f
feat(landing): single agent cast + npx scaffold pill in hero
buremba May 19, 2026
2414d63
feat(landing): drop Start building nav CTA
buremba May 19, 2026
97976ba
feat(landing): wrap hero cast in terminal-window chrome with npx pill
buremba May 19, 2026
77063c3
fix(landing): trim first 5s of dead time from agent cast
buremba May 19, 2026
4e85ac0
feat(landing): memory column with tabular entities widget, move react…
buremba May 19, 2026
cdf7d48
feat(landing): gate hero cast on hover (desktop) or 5s timer (mobile)
buremba May 19, 2026
8b23a9d
fix(landing): zero leading delay on hero cast so first frame isn't blank
buremba May 19, 2026
808361d
fix(landing): drop "claude code" from terminal title, dock label next…
buremba May 19, 2026
514a9db
fix(landing): trim hero cast to scaffold + validate only
buremba May 19, 2026
2370627
Revert "fix(landing): trim hero cast to scaffold + validate only"
buremba May 19, 2026
ca600e5
fix(landing): trim hero cast at t=35s after validate-success
buremba May 19, 2026
be188e9
feat(landing): swap pinned connector to stripe-charges for relatability
buremba May 19, 2026
aae8df8
docs(landing): connector-sdk + reaction-sdk guides and references
buremba May 19, 2026
008f05d
fix(landing): unified 5s autoplay timer that cancels on manual play
buremba May 19, 2026
79d450b
docs(landing): rewrite SDK examples — fully typed, hand-written, no `…
buremba May 20, 2026
b298664
Merge remote-tracking branch 'origin/main' into feat/landing-dev-focus
buremba May 20, 2026
b760a85
fix(examples): make stripe-charges connector match @lobu/connector-sd…
buremba May 20, 2026
d5a9ea8
docs(landing): fix env_keys credential access in connector-sdk guides
buremba May 20, 2026
371cc9f
docs(landing): reframe reactions as a surface of @lobu/connector-sdk
buremba May 20, 2026
314e219
fix(landing): make HeroAsciinema accessible to screen readers + no-JS
buremba May 20, 2026
6f9f3d1
fix(landing): label demo data as placeholders, not real customer output
buremba May 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion config/biome.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
"!**/apps/**",
"!**/scripts/lobu/**",
"!**/benchmarks/**",
"!**/skills/**"
"!**/skills/**",
"!**/packages/landing/public/**"
]
},
"formatter": {
Expand Down
33 changes: 33 additions & 0 deletions examples/agent-community/connectors/discourse-posts.connector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// biome-ignore-all format: stays compact for the landing-page code panel
import { ConnectorRuntime, type SyncContext } from "@lobu/connector-sdk";

export default class DiscoursePostsConnector extends ConnectorRuntime {
readonly definition = {
key: "discourse-posts",
name: "Discourse posts",
version: "1.0.0",
authSchema: { methods: [{ type: "env" as const, fields: [{ name: "api_key" }] }] },
feeds: { posts: { key: "posts", name: "Forum posts" } },
};

async sync(ctx: SyncContext) {
const cursor = (ctx.checkpoint as any)?.last_post_id ?? 0;
const r = await fetch(`${ctx.config.base_url}/posts.json?before=${cursor + 50}`);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
const posts: any[] = ((await r.json() as any).latest_posts ?? []).filter((p: any) => p.id > cursor).sort((a: any, b: any) => a.id - b.id);
Comment on lines +14 to +16
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

find . -name "discourse-posts.connector.ts" -type f

Repository: lobu-ai/lobu

Length of output: 123


🏁 Script executed:

cat -n ./examples/agent-community/connectors/discourse-posts.connector.ts

Repository: lobu-ai/lobu

Length of output: 1596


🏁 Script executed:

# Check if there are other files in the connector directory to understand the pattern
find ./examples/agent-community/connectors -type f -name "*.ts" | head -5

Repository: lobu-ai/lobu

Length of output: 123


🌐 Web query:

Discourse API posts.json before parameter pagination behavior

💡 Result:

In Discourse’s posts.json (the “list latest posts across topics” API), pagination is done with the query parameter before (a cursor), not with a page= offset. The docs say: “Load posts with an id lower than this value. Useful for pagination,” and the parameter table lists before for get/posts.json. [1] So the intended usage is: - Start with no before parameter (you get the most recent posts). - For the next page, send before= where is the last (oldest) post id you received, and Discourse returns posts with ids lower than that value. [1] If you instead try page=2 with /posts.json, you may find it doesn’t work; a Discourse Meta discussion notes you might need to use /latest.json?page=2 rather than /posts.json (because /posts.json isn’t the right endpoint for page-style pagination). [2] Example (conceptual): - First call: https:///posts.json - Next call: https:///posts.json?before=<oldest_post_id_from_previous_batch> That’s the “before” behavior for posts.json pagination in Discourse.

Citations:


🏁 Script executed:

# Check file context - is this an example or production code?
head -5 ./examples/agent-community/connectors/discourse-posts.connector.ts && \
# Check if there's a README or other docs explaining this
find ./examples/agent-community -name "*.md" -o -name "README*" | head -5

Repository: lobu-ai/lobu

Length of output: 481


Cursor window logic skips posts and auth key is unused.

Line 15 fetches posts with before=${cursor + 50}, and Line 16 filters to id > cursor. If more than 50 posts arrive between runs, those beyond the window are never read. Additionally, the api_key declared in the authSchema (line 9) is never used in the fetch request—add the API key to the request headers or query parameters.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/agent-community/connectors/discourse-posts.connector.ts` around
lines 14 - 16, The current cursor/window logic uses before=${cursor + 50} and
then filters id > cursor which can permanently skip posts if >50 new posts
arrive; replace that with a proper "since" style fetch and pagination: request
posts using after=${cursor} (or iterate fetching pages until no posts > cursor)
and remove the id>cursor post-filter, updating the checkpoint to the highest
returned post id (reference variables: cursor, ctx.checkpoint, the fetch call).
Also include the configured API key from your connector auth (use
ctx.secrets?.api_key or the authSchema key) in the request (e.g., add an Api-Key
header or query param when calling fetch against ctx.config.base_url) so
authentication is actually sent. Ensure to update the fetch invocation and
checkpoint handling in the same function accordingly.

return {
events: posts.map((p) => ({
origin_id: String(p.id),
origin_type: "post_created",
title: p.topic_title ?? `Post by ${p.username}`,
author_name: p.username,
source_url: `${ctx.config.base_url}/t/${p.topic_slug}/${p.topic_id}/${p.id}`,
occurred_at: new Date(p.created_at),
})),
checkpoint: { last_post_id: posts.at(-1)?.id ?? cursor },
};
}

async execute() {
return { success: false, error: "no actions" };
}
}
32 changes: 32 additions & 0 deletions examples/delivery/connectors/shopify-orders.connector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// biome-ignore-all format: stays compact for the landing-page code panel
import { ConnectorRuntime, type SyncContext } from "@lobu/connector-sdk";

export default class ShopifyOrdersConnector extends ConnectorRuntime {
readonly definition = {
key: "shopify-orders",
name: "Shopify orders",
version: "1.0.0",
authSchema: { methods: [{ type: "env" as const, fields: [{ name: "access_token" }] }] },
feeds: { orders: { key: "orders", name: "Order updates" } },
};

async sync(ctx: SyncContext) {
const since = (ctx.checkpoint as any)?.updated_at_min ?? "2000-01-01T00:00:00Z";
const r = await fetch(`https://${ctx.config.shop}/admin/api/2024-10/orders.json?status=any&updated_at_min=${encodeURIComponent(since)}&limit=100`);
const orders: any[] = ((await r.json() as any).orders ?? []).sort((a: any, b: any) => new Date(a.updated_at).getTime() - new Date(b.updated_at).getTime());
Comment on lines +9 to +16
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

cat -n examples/delivery/connectors/shopify-orders.connector.ts

Repository: lobu-ai/lobu

Length of output: 1697


access_token is defined in authSchema but never used in the API call.

Line 9 declares env auth, but line 15 sends the Shopify Admin API request without the token, leaving sync unwired to its own auth contract.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/delivery/connectors/shopify-orders.connector.ts` around lines 9 -
16, The sync function is not using the access_token declared in authSchema;
update sync to read the token from ctx.auth or ctx.config (where auth is stored)
and include it as the Authorization header (Bearer <access_token>) or as
X-Shopify-Access-Token per Shopify API when performing fetch to orders.json;
modify the fetch call in sync to add the appropriate header and ensure it throws
or logs a clear error if access_token is missing, referencing authSchema and the
sync function so reviewers can find the change.

return {
events: orders.map((o) => ({
origin_id: `${o.id}:${o.updated_at}`,
origin_type: "order_updated",
title: `Order ${o.name} — ${o.fulfillment_status ?? "unfulfilled"}`,
source_url: `https://${ctx.config.shop}/admin/orders/${o.id}`,
occurred_at: new Date(o.updated_at),
})),
checkpoint: { updated_at_min: orders.at(-1)?.updated_at ?? since },
};
}

async execute() {
return { success: false, error: "no actions" };
}
}
39 changes: 39 additions & 0 deletions examples/ecommerce/connectors/stripe-charges.connector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// biome-ignore-all format: stays compact for the landing-page code panel
import { ConnectorRuntime, type ConnectorDefinition, type EventEnvelope, type SyncContext, type SyncResult } from "@lobu/connector-sdk";

interface StripeCharge { id: string; amount: number; currency: string; created: number; refunded: boolean }
interface Checkpoint { last_created: number }

export default class StripeChargesConnector extends ConnectorRuntime {
readonly definition: ConnectorDefinition = {
key: "stripe-charges",
name: "Stripe charges",
version: "1.0.0",
// Stripe secret key collected per connection; exposed to sync() as ctx.config.secret_key.
authSchema: { methods: [{ type: "env_keys", fields: [{ key: "secret_key", label: "Stripe secret key", secret: true, required: true }] }] },
feeds: { charges: { key: "charges", name: "Charges" } },
};

async sync(ctx: SyncContext): Promise<SyncResult> {
const cursor = ((ctx.checkpoint ?? {}) as Partial<Checkpoint>).last_created ?? 0;
const secretKey = String(ctx.config.secret_key ?? "");
const r = await fetch(`https://api.stripe.com/v1/charges?limit=100&created[gt]=${cursor}`, {
headers: { Authorization: `Bearer ${secretKey}` },
});
if (!r.ok) throw new Error(`Stripe ${r.status}: ${await r.text()}`);
const data = (((await r.json()) as { data?: StripeCharge[] }).data ?? []).sort((a, b) => a.created - b.created);
const events: EventEnvelope[] = data.map((c) => ({
origin_id: c.refunded ? `${c.id}:refund` : c.id,
origin_type: c.refunded ? "charge_refunded" : "charge_succeeded",
title: `${c.refunded ? "Refund" : "Charge"} — ${(c.amount / 100).toFixed(2)} ${c.currency.toUpperCase()}`,
payload_text: `${c.refunded ? "Refund" : "Charge"} of ${(c.amount / 100).toFixed(2)} ${c.currency.toUpperCase()} (stripe id ${c.id})`,
source_url: `https://dashboard.stripe.com/payments/${c.id}`,
occurred_at: new Date(c.created * 1000),
}));
return { events, checkpoint: { last_created: data.at(-1)?.created ?? cursor } satisfies Checkpoint };
}

async execute() {
return { success: false, error: "no actions" };
}
}
32 changes: 32 additions & 0 deletions examples/finance/connectors/quickbooks-transactions.connector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// biome-ignore-all format: stays compact for the landing-page code panel
import { ConnectorRuntime, type SyncContext } from "@lobu/connector-sdk";

export default class QuickBooksTransactionsConnector extends ConnectorRuntime {
readonly definition = {
key: "quickbooks-transactions",
name: "QuickBooks transactions",
version: "1.0.0",
authSchema: { methods: [{ type: "oauth" as const, provider: "intuit" }] },
feeds: { transactions: { key: "transactions", name: "Posted transactions" } },
};

async sync(ctx: SyncContext) {
const since = (ctx.checkpoint as any)?.last_txn_date ?? "1970-01-01";
const q = `SELECT * FROM Transaction WHERE TxnDate > '${since}' ORDERBY TxnDate ASC MAXRESULTS 500`;
const r = await fetch(`https://quickbooks.api.intuit.com/v3/company/${ctx.config.realm_id}/query?query=${encodeURIComponent(q)}`);
Comment on lines +9 to +16
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

# First, locate and examine the file
find . -type f -name "quickbooks-transactions.connector.ts" | head -5

Repository: lobu-ai/lobu

Length of output: 123


🏁 Script executed:

# Once found, examine the full file
cat -n examples/finance/connectors/quickbooks-transactions.connector.ts

Repository: lobu-ai/lobu

Length of output: 1643


🏁 Script executed:

# Search for SyncContext definition in connector-sdk
rg "type SyncContext|interface SyncContext|class SyncContext" -A 20

Repository: lobu-ai/lobu

Length of output: 50370


🏁 Script executed:

# Look for other connector examples that might show proper OAuth usage
find . -type f -name "*.connector.ts" | head -10

Repository: lobu-ai/lobu

Length of output: 602


🏁 Script executed:

# Check if there are any other connectors using oauth
rg "oauth.*provider" --type ts

Repository: lobu-ai/lobu

Length of output: 7716


🏁 Script executed:

# Check another OAuth connector to see the proper pattern
cat -n examples/sales/connectors/salesforce-pipeline.connector.ts | head -30

Repository: lobu-ai/lobu

Length of output: 1574


🏁 Script executed:

# Search for how credentials are used in the connector runtime
rg "credentials" packages/connector-sdk/src/connector-types.ts -A 5 -B 5

Repository: lobu-ai/lobu

Length of output: 2366


🏁 Script executed:

# Check for fetch wrapper or middleware that might inject auth
rg "Authorization.*accessToken|accessToken.*Authorization" -A 3 -B 3

Repository: lobu-ai/lobu

Length of output: 8101


Add Authorization header to fetch request using credentials from context.

Line 16 declares OAuth for Intuit but sends the request without passing credentials. The SyncContext provides credentials?.accessToken which should be included in the fetch headers. Compare against working OAuth connectors like Spotify or YouTube which explicitly add Authorization: Bearer ${accessToken} headers—the QuickBooks connector is missing this step.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/finance/connectors/quickbooks-transactions.connector.ts` around
lines 9 - 16, The fetch to QuickBooks in the async sync(ctx: SyncContext)
function is missing the OAuth Authorization header; update the fetch call that
builds the query URL for ctx.config.realm_id to include headers: pass
Authorization: Bearer ${ctx.credentials?.accessToken} (or
ctx.credentials.accessToken) and appropriate Accept/Content-Type as needed so
the request uses the OAuth token from SyncContext credentials; ensure you
reference the existing q variable and keep encodeURIComponent(q) when adding
headers to the same fetch invocation.

Comment on lines +14 to +16
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

cat -n examples/finance/connectors/quickbooks-transactions.connector.ts

Repository: lobu-ai/lobu

Length of output: 1643


Use timestamp or sequence granularity for checkpoint to avoid skipping same-day transaction overflow.

With MAXRESULTS 500 (line 15) and TxnDate > since filter (line 15), if more than 500 transactions occur on a single date, the checkpoint set to last_txn_date (line 25) will skip remaining transactions on that date on the next sync (they won't satisfy the strictly-greater-than filter). Update checkpoint to include time or a transaction sequence number for sub-day granularity.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/finance/connectors/quickbooks-transactions.connector.ts` around
lines 14 - 16, The current checkpoint (ctx.checkpoint?.last_txn_date) and query
(TxnDate > since with MAXRESULTS 500) can skip same-day overflow; change the
checkpoint to include sub-day granularity (e.g., last_txn_datetime or a tuple
{last_txn_date, last_txn_id}) and update the query construction in this
connector where variables since, q and the fetch call are created: use a
timestamp field (MetaData.LastUpdatedTime or TxnDate with time) or a composite
filter to break ties (e.g., fetch transactions where TxnDate > last_date OR
(TxnDate = last_date AND Id > last_id)) so the next run includes remaining
same-day records, and when persisting the checkpoint save both the
datetime/timestamp or datetime+id so subsequent queries use the finer-grained
checkpoint.

const txns: any[] = (await r.json() as any).QueryResponse?.Transaction ?? [];
return {
events: txns.map((t) => ({
origin_id: t.Id,
origin_type: "transaction_posted",
title: `${t.AccountRef?.name ?? "Bank"} — $${t.Amount.toFixed(2)}`,
occurred_at: new Date(`${t.TxnDate}T00:00:00Z`),
})),
checkpoint: { last_txn_date: txns.at(-1)?.TxnDate ?? since },
};
}

async execute() {
return { success: false, error: "no actions" };
}
}
34 changes: 34 additions & 0 deletions examples/leadership/connectors/linear-cycles.connector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// biome-ignore-all format: stays compact for the landing-page code panel
import { ConnectorRuntime, type SyncContext } from "@lobu/connector-sdk";

const QUERY = `query($t:ID!,$a:DateTimeOrDuration!){issues(first:100,filter:{team:{id:{eq:$t}},updatedAt:{gt:$a},cycle:{isActive:{eq:true}}},orderBy:updatedAt){nodes{id identifier title url updatedAt state{name}}}}`;

export default class LinearCyclesConnector extends ConnectorRuntime {
readonly definition = {
key: "linear-cycles",
name: "Linear cycles",
version: "1.0.0",
authSchema: { methods: [{ type: "oauth" as const, provider: "linear" }] },
feeds: { cycle_issues: { key: "cycle_issues", name: "Cycle issues" } },
};

async sync(ctx: SyncContext) {
const since = (ctx.checkpoint as any)?.updated_at ?? "2000-01-01T00:00:00Z";
const r = await fetch("https://api.linear.app/graphql", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ query: QUERY, variables: { t: ctx.config.team_id, a: since } }) });
Comment on lines +11 to +17
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

cat -n examples/leadership/connectors/linear-cycles.connector.ts

Repository: lobu-ai/lobu

Length of output: 1846


🏁 Script executed:

find . -type f -name "*.ts" -o -name "*.tsx" | xargs grep -l "SyncContext" | head -20

Repository: lobu-ai/lobu

Length of output: 1179


🏁 Script executed:

rg "class.*Connector.*extends ConnectorRuntime" -A 20 | head -100

Repository: lobu-ai/lobu

Length of output: 13551


🏁 Script executed:

cat -n packages/connector-sdk/src/connector-types.ts | head -150

Repository: lobu-ai/lobu

Length of output: 5887


🏁 Script executed:

rg "interface SyncContext|type SyncContext" -A 15 packages/connector-sdk/

Repository: lobu-ai/lobu

Length of output: 1560


🏁 Script executed:

rg "interface SyncCredentials|type SyncCredentials" -A 10 packages/connector-sdk/

Repository: lobu-ai/lobu

Length of output: 776


🏁 Script executed:

rg "ctx\.credentials" examples/ packages/connectors/ -B 2 -A 2

Repository: lobu-ai/lobu

Length of output: 7199


Add Authorization header using OAuth credentials from context.

Line 11 declares Linear OAuth, but Line 17 executes the GraphQL request without applying credentials. Every OAuth-enabled connector in the codebase extracts the access token from ctx.credentials?.accessToken and includes it in the request. The Linear API requires authentication and will fail without it.

Add the access token to the Authorization header:

const accessToken = ctx.credentials?.accessToken;
const r = await fetch("https://api.linear.app/graphql", { 
  method: "POST", 
  headers: { 
    "Content-Type": "application/json",
    ...(accessToken ? { "Authorization": `Bearer ${accessToken}` } : {})
  }, 
  body: JSON.stringify({ query: QUERY, variables: { t: ctx.config.team_id, a: since } }) 
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/leadership/connectors/linear-cycles.connector.ts` around lines 11 -
17, In the sync(ctx: SyncContext) function the GraphQL POST to
"https://api.linear.app/graphql" omits the OAuth token; read
ctx.credentials?.accessToken and add an Authorization: Bearer <token> header
when present (merge with existing "Content-Type" header) before calling fetch so
the Linear API request is authenticated.

const issues: any[] = ((await r.json()) as any).data?.issues?.nodes ?? [];
return {
events: issues.map((i) => ({
origin_id: `${i.id}:${i.state.name}`,
origin_type: "issue_state_changed",
title: `${i.identifier} ${i.title} → ${i.state.name}`,
source_url: i.url,
occurred_at: new Date(i.updatedAt),
})),
checkpoint: { updated_at: issues.at(-1)?.updatedAt ?? since },
};
}

async execute() {
return { success: false, error: "no actions" };
}
}
32 changes: 32 additions & 0 deletions examples/legal/connectors/docusign-envelopes.connector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// biome-ignore-all format: stays compact for the landing-page code panel
import { ConnectorRuntime, type SyncContext } from "@lobu/connector-sdk";

export default class DocuSignEnvelopesConnector extends ConnectorRuntime {
readonly definition = {
key: "docusign-envelopes",
name: "DocuSign envelopes",
version: "1.0.0",
authSchema: { methods: [{ type: "oauth" as const, provider: "docusign" }] },
feeds: { envelopes: { key: "envelopes", name: "Envelope status changes" } },
};

async sync(ctx: SyncContext) {
const since = (ctx.checkpoint as any)?.last_status_changed ?? "2000-01-01T00:00:00Z";
const base = String(ctx.config.base_path ?? "https://www.docusign.net/restapi").replace(/\/$/, "");
const r = await fetch(`${base}/v2.1/accounts/${ctx.config.account_id}/envelopes?from_date=${encodeURIComponent(since)}&count=100`);
Comment on lines +9 to +16
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

cat -n examples/legal/connectors/docusign-envelopes.connector.ts

Repository: lobu-ai/lobu

Length of output: 1799


🏁 Script executed:

find . -type f -name "*.connector.ts" | head -10

Repository: lobu-ai/lobu

Length of output: 602


🏁 Script executed:

rg "authSchema.*oauth" --type ts -A 5 -B 2 | head -60

Repository: lobu-ai/lobu

Length of output: 3328


🏁 Script executed:

rg "class.*extends ConnectorRuntime" --type ts -l | head -5

Repository: lobu-ai/lobu

Length of output: 256


🏁 Script executed:

cat -n examples/sales/connectors/salesforce-pipeline.connector.ts | head -30

Repository: lobu-ai/lobu

Length of output: 1574


🏁 Script executed:

cat -n examples/finance/connectors/quickbooks-transactions.connector.ts | head -30

Repository: lobu-ai/lobu

Length of output: 1623


🏁 Script executed:

cat -n examples/leadership/connectors/linear-cycles.connector.ts | head -30

Repository: lobu-ai/lobu

Length of output: 1740


🏁 Script executed:

find . -type f -name "*.ts" -path "*/connector-sdk/*" | head -20

Repository: lobu-ai/lobu

Length of output: 992


🏁 Script executed:

rg "type SyncContext" --type ts -B 2 -A 10 | head -50

Repository: lobu-ai/lobu

Length of output: 2842


🏁 Script executed:

cat -n packages/connector-sdk/src/connector-types.ts | head -100

Repository: lobu-ai/lobu

Length of output: 3933


🏁 Script executed:

cat -n packages/connector-sdk/src/types.ts | head -100

Repository: lobu-ai/lobu

Length of output: 3987


🏁 Script executed:

rg "interface SyncContext" --type ts -B 2 -A 20

Repository: lobu-ai/lobu

Length of output: 4026


🏁 Script executed:

rg "export.*SyncContext" --type ts -B 2 -A 15 | head -80

Repository: lobu-ai/lobu

Length of output: 3356


Add Authorization header using OAuth credentials from context.

authSchema declares DocuSign OAuth on Line 9, but the API call at Line 16 doesn't use ctx.credentials.accessToken to authorize the request. Set the Authorization header: headers: { Authorization: \Bearer ${ctx.credentials?.accessToken}` }`.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/legal/connectors/docusign-envelopes.connector.ts` around lines 9 -
16, The sync method is calling the DocuSign API without using the OAuth token
declared in authSchema; update the fetch in sync to include an Authorization
header using ctx.credentials?.accessToken (e.g., headers: { Authorization:
`Bearer ${ctx.credentials?.accessToken}` }) and preserve existing parameters
(from_date, count) and base URL handling; ensure you handle missing accessToken
gracefully (throw or log) before making the request.

const envelopes: any[] = ((await r.json() as any).envelopes ?? []).sort((a: any, b: any) => new Date(a.statusChangedDateTime).getTime() - new Date(b.statusChangedDateTime).getTime());
return {
events: envelopes.map((e) => ({
origin_id: `${e.envelopeId}:${e.status}`,
origin_type: "envelope_status_changed",
title: `${e.emailSubject ?? e.envelopeId} → ${e.status}`,
occurred_at: new Date(e.statusChangedDateTime),
})),
checkpoint: { last_status_changed: envelopes.at(-1)?.statusChangedDateTime ?? since },
};
}

async execute() {
return { success: false, error: "no actions" };
}
}
Loading
Loading