Skip to content

AGENTS.md

118 lines · 3.8 KB · Markdown
Raw

@kody/meta — agent notes

Human setup and intent live in README.md. This file is for agents: imports, smoke/dryRun execute snippets, and edge cases. Secrets and OAuth by name only — never paste tokens. Do not disable live webhooks or jobs. Never hard-code Page, Instagram, or phone-number ids.

Auth

CallResolves to
omitOAuth meta
account: 'work'meta-work / metaSystemUserToken-work
integration: 'meta-work'that exact OAuth name
authMode: 'system-user'secret metaSystemUserToken (or tokenSecret)
tokenSecret: 'metaSystemUserToken-work'that exact system-user secret

Hosts: graph.facebook.com, graph.instagram.com, graph-video.facebook.com. API base: https://graph.facebook.com/v26.0.

Import paths

ExportImport
overview + smokeTestkody:@kody/meta
accountskody:@kody/meta/accounts
smoke-testkody:@kody/meta/smoke-test
scopeskody:@kody/meta/scopes
core (graphRequest)kody:@kody/meta/core
pageskody:@kody/meta/pages
instagramkody:@kody/meta/instagram
whatsappkody:@kody/meta/whatsapp
requestkody:@kody/meta/request
typeskody:@kody/meta/types

Prefer static kody:@kody/meta/... imports from execute. Do not lead with packages.invoke. Named helpers also re-export from product modules (listPages, publishPost, listInstagramAccounts, sendMessage, …).

Smoke test (read-only)

import smokeTest from 'kody:@kody/meta/smoke-test'

export default async function main() {
	return await smokeTest()
	// or: smokeTest({ integration: 'meta-work' })
	// or: smokeTest({ authMode: 'system-user' })
	// ok => { ok: true, authMode, integration, id, name } (no post/send)
	// missing creds => { ok: false, setup: { …URLs } }
}

Common snippets

import { listPages } from 'kody:@kody/meta/pages'
import { listInstagramAccounts } from 'kody:@kody/meta/instagram'
import { listBusinesses } from 'kody:@kody/meta/whatsapp'

export default async function main() {
	const pages = await listPages({ limit: 5 })
	const ig = await listInstagramAccounts()
	const businesses = await listBusinesses()
	return { pages, ig, businesses }
}

Escape hatch:

import request from 'kody:@kody/meta/request'

export default async function main() {
	return await request({ path: '/me', query: { fields: 'id,name' } })
}

dryRun / confirm

publishPost, publishMedia, sendMessage, and mutating ./request / graphRequest require confirm: true. Pass dryRun: true to preview without writing.

import { listPages, publishPost } from 'kody:@kody/meta/pages'
import { listPhoneNumbers, sendMessage } from 'kody:@kody/meta/whatsapp'

export default async function main() {
	const pages = await listPages({ limit: 5 })
	const preview = await publishPost({
		pageId: pages.items[0]?.id,
		message: 'Preview only',
		dryRun: true,
	})
	const phones = await listPhoneNumbers({})
	const waPreview = await sendMessage({
		phoneNumberId: phones.items?.[0]?.id,
		to: '15551234567',
		body: 'Preview only',
		dryRun: true,
	})
	return { preview, waPreview }
}

Resolve WhatsApp phoneNumberId from listPhoneNumbers — never a hard-coded brand number. After the user approves the exact target + body, retry with confirm: true (not only dryRun: false).

Edge cases

  • 403 / missing permission errors name the scope and the next /connect/oauth URL; do not hide Page / IG / WhatsApp helpers when a scope is missing.
  • User OAuth tokens expire; prefer authMode: 'system-user' for unattended jobs.
  • Do not scrape personal Facebook profiles or paste tokens into chat.
  • Package-owned jobs are not enabled on this package — do not invent schedules that disable or rewrite live webhooks.