From 1427e01b61f6dd3feca54910f2c956bca90e26c9 Mon Sep 17 00:00:00 2001 From: Gaonuk Date: Tue, 7 Jul 2026 13:16:28 -0400 Subject: [PATCH 1/2] feat: add plugin logs search support --- .claude-plugin/marketplace.json | 4 +- .cursor-plugin/marketplace.json | 4 +- README.md | 4 +- plugins/kapso/.claude-plugin/plugin.json | 2 +- plugins/kapso/.codex-plugin/plugin.json | 8 +- plugins/kapso/.cursor-plugin/plugin.json | 2 +- plugins/kapso/CHANGELOG.md | 4 + plugins/kapso/README.md | 6 +- .../kapso/skills/observe-whatsapp/SKILL.md | 49 ++- .../references/log-search-reference.md | 80 +++++ .../references/triage-reference.md | 14 +- .../scripts/log-search-catalog.js | 36 +++ .../observe-whatsapp/scripts/log-search.js | 299 ++++++++++++++++++ 13 files changed, 477 insertions(+), 35 deletions(-) create mode 100644 plugins/kapso/skills/observe-whatsapp/references/log-search-reference.md create mode 100644 plugins/kapso/skills/observe-whatsapp/scripts/log-search-catalog.js create mode 100644 plugins/kapso/skills/observe-whatsapp/scripts/log-search.js diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 42ce8cd..739527c 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -2,7 +2,7 @@ "$schema": "https://json.schemastore.org/claude-code-marketplace.json", "name": "kapso", "version": "0.1.0", - "description": "Kapso plugins for Claude Code, including WhatsApp automation, integration, and observability skills.", + "description": "Kapso plugins for Claude Code, including WhatsApp automation, integration, log search, and observability skills.", "owner": { "name": "Kapso", "url": "https://kapso.ai" @@ -10,7 +10,7 @@ "plugins": [ { "name": "kapso", - "description": "Build, integrate, and observe Kapso WhatsApp agents.", + "description": "Build, integrate, search logs, and observe Kapso WhatsApp agents.", "version": "0.1.0", "author": { "name": "Kapso", diff --git a/.cursor-plugin/marketplace.json b/.cursor-plugin/marketplace.json index 2ebd9ba..701faaf 100644 --- a/.cursor-plugin/marketplace.json +++ b/.cursor-plugin/marketplace.json @@ -5,13 +5,13 @@ "email": "dev@kap.so" }, "metadata": { - "description": "Kapso agent plugins for building, integrating, and observing WhatsApp agents." + "description": "Kapso agent plugins for building, integrating, searching logs, and observing WhatsApp agents." }, "plugins": [ { "name": "kapso", "source": "plugins/kapso", - "description": "Build, integrate, and observe Kapso WhatsApp agents." + "description": "Build, integrate, search logs, and observe Kapso WhatsApp agents." } ] } diff --git a/README.md b/README.md index daa136b..c592eff 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Missing an agent harness? Open an issue in this repository. - `integrate-whatsapp`: connect WhatsApp to products, onboard customers, configure webhooks, send messages, manage templates, and work with WhatsApp Flows. - `automate-whatsapp`: build workflows, triggers, functions, agents, app integrations, and database-backed automations. -- `observe-whatsapp`: inspect delivery, webhook retries, API errors, number health, templates, and operational incidents. +- `observe-whatsapp`: search unified project logs, inspect delivery, webhook retries, API errors, workflow events, number health, templates, and operational incidents. - Kapso MCP server configs for remote authenticated access to Kapso. - Safety guidance, examples, and validation scripts for release checks. @@ -71,7 +71,7 @@ codex plugin install kapso@kapso codex mcp login kapso ``` -Direct API fallback scripts use: +Direct API fallback scripts, including unified log search, use: ```bash export KAPSO_API_BASE_URL="https://api.kapso.ai" diff --git a/plugins/kapso/.claude-plugin/plugin.json b/plugins/kapso/.claude-plugin/plugin.json index 697a5de..e7767fb 100644 --- a/plugins/kapso/.claude-plugin/plugin.json +++ b/plugins/kapso/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "kapso", - "description": "Build, integrate, and observe Kapso WhatsApp agents with Claude Code.", + "description": "Build, integrate, search logs, and observe Kapso WhatsApp agents with Claude Code.", "version": "0.1.0", "author": { "name": "Kapso", diff --git a/plugins/kapso/.codex-plugin/plugin.json b/plugins/kapso/.codex-plugin/plugin.json index 1f1e77c..b6e132c 100644 --- a/plugins/kapso/.codex-plugin/plugin.json +++ b/plugins/kapso/.codex-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "kapso", "version": "0.1.0", - "description": "Build, integrate, and observe Kapso WhatsApp agents with Codex.", + "description": "Build, integrate, search logs, and observe Kapso WhatsApp agents with Codex.", "author": { "name": "Kapso", "email": "dev@kap.so", @@ -21,8 +21,8 @@ "mcpServers": "./.mcp.json", "interface": { "displayName": "Kapso", - "shortDescription": "Build, integrate, and observe Kapso WhatsApp agents from Codex.", - "longDescription": "Kapso is the WhatsApp API for developers. This plugin helps Codex onboard customers to WhatsApp, send and receive messages, manage templates and flows, build workflow automations, deploy functions, and debug production delivery or webhook issues with focused Kapso context.", + "shortDescription": "Build, integrate, search logs, and observe Kapso WhatsApp agents from Codex.", + "longDescription": "Kapso is the WhatsApp API for developers. This plugin helps Codex onboard customers to WhatsApp, send and receive messages, manage templates and flows, build workflow automations, deploy functions, and debug production delivery, workflow, API, or webhook issues with unified log search and focused Kapso context.", "developerName": "Kapso", "category": "Developer Tools", "capabilities": [ @@ -32,7 +32,7 @@ ], "defaultPrompt": [ "Set up WhatsApp onboarding", - "Check WhatsApp health", + "Search project logs", "Build a WhatsApp support agent" ], "websiteURL": "https://kapso.ai", diff --git a/plugins/kapso/.cursor-plugin/plugin.json b/plugins/kapso/.cursor-plugin/plugin.json index 1011cc8..86b5101 100644 --- a/plugins/kapso/.cursor-plugin/plugin.json +++ b/plugins/kapso/.cursor-plugin/plugin.json @@ -2,7 +2,7 @@ "name": "kapso", "displayName": "Kapso", "version": "0.1.0", - "description": "Build, integrate, and observe Kapso WhatsApp agents with Cursor.", + "description": "Build, integrate, search logs, and observe Kapso WhatsApp agents with Cursor.", "author": { "name": "Kapso", "email": "dev@kap.so" diff --git a/plugins/kapso/CHANGELOG.md b/plugins/kapso/CHANGELOG.md index 0d3897a..5c1d30c 100644 --- a/plugins/kapso/CHANGELOG.md +++ b/plugins/kapso/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## Unreleased + +- Added unified project log search guidance and fallback scripts for Logs V2. + ## 0.1.0 - Added skills for integrating WhatsApp, automating WhatsApp workflows, and observing WhatsApp delivery or webhook issues. diff --git a/plugins/kapso/README.md b/plugins/kapso/README.md index 96bb063..30cc599 100644 --- a/plugins/kapso/README.md +++ b/plugins/kapso/README.md @@ -8,7 +8,7 @@ Kapso is the WhatsApp API for developers. This plugin helps agents build, integr - Connect WhatsApp to products with setup links, connection detection, webhooks, sends, templates, media, and WhatsApp Flows. - Build Kapso workflows with WhatsApp triggers, AI steps, functions, app integrations, data tables, and execution controls. -- Observe production issues by inspecting message delivery, webhook retries, API logs, template health, number health, and error patterns. +- Observe production issues with unified project log search across API calls, Meta events, workflow events, webhook deliveries, message delivery, template health, number health, and error patterns. - Use bundled examples and references so agents can act with product-specific context instead of generic WhatsApp guidance. - Keep risky operations behind explicit user approval for sends, deploys, deletes, webhook changes, template creation, setup links, and workflow mutations. @@ -32,7 +32,7 @@ codex plugin install kapso@kapso - Skills: - `integrate-whatsapp`: connect WhatsApp to products, onboard customers, configure webhooks, send messages, manage templates, and work with WhatsApp Flows. - `automate-whatsapp`: build workflows, triggers, functions, agents, app integrations, and database-backed automations. - - `observe-whatsapp`: inspect delivery, webhook retries, API errors, number health, templates, and operational incidents. + - `observe-whatsapp`: search unified project logs, inspect delivery, webhook retries, API errors, workflow events, number health, templates, and operational incidents. - Rule: - `kapso-safety`: classifies read-only, local write, and high-risk write operations, and requires explicit approval before high-risk writes. - MCP: @@ -121,7 +121,7 @@ User prompt: "Why did this WhatsApp message fail, and did the webhook retry?" Expected behavior: - The agent uses the `observe-whatsapp` skill. -- It gathers message details, delivery history, API errors, webhook deliveries, and number health. +- It starts with unified log search, then gathers message details, delivery history, API errors, webhook deliveries, and number health as needed. - It returns a concise diagnosis with next actions and escalation paths. ## Safety diff --git a/plugins/kapso/skills/observe-whatsapp/SKILL.md b/plugins/kapso/skills/observe-whatsapp/SKILL.md index 0becde0..b5124ca 100644 --- a/plugins/kapso/skills/observe-whatsapp/SKILL.md +++ b/plugins/kapso/skills/observe-whatsapp/SKILL.md @@ -1,13 +1,13 @@ --- name: observe-whatsapp -description: "Observe and troubleshoot WhatsApp in Kapso: debug message delivery, inspect webhook deliveries/retries, triage API errors, and run health checks. Use when investigating production issues, message failures, or webhook delivery problems." +description: "Observe and troubleshoot WhatsApp in Kapso: search operational logs, debug message delivery, inspect webhook deliveries/retries, triage API errors, and run health checks. Use when investigating production issues, message failures, API calls, workflow execution issues, or webhook delivery problems." --- # Observe WhatsApp ## When to use -Use this skill for operational diagnostics: message delivery investigation, webhook delivery debugging, error triage, and WhatsApp health checks. +Use this skill for operational diagnostics: unified project log search, message delivery investigation, webhook delivery debugging, error triage, workflow execution investigation, and WhatsApp health checks. ## Setup @@ -38,14 +38,31 @@ Fallback path: ### Triage errors Preferred path: -1. Confirm project and number state: `kapso status` -2. Run number health: `kapso whatsapp numbers health --phone-number "" --output human` -3. Inspect related templates when relevant: `kapso whatsapp templates list --phone-number "" --output json` +1. Search cross-source logs first when you have a request ID, `wamid.*`, endpoint, webhook ID, workflow execution ID, phone number, or recent time window: + `kapso logs search --query "" --period 24h --limit 20 --output json` +2. Narrow by source when known: + `kapso logs search --source external_api_log --query "/messages" --problems-only --output json` +3. Confirm project and number state: `kapso status` +4. Run number health: `kapso whatsapp numbers health --phone-number "" --output human` +5. Inspect related templates when relevant: `kapso whatsapp templates list --phone-number "" --output json` + +MCP path: +- If the Kapso MCP server is connected, use `search_logs` for cross-resource diagnostics before older narrow tools. +- Good starting inputs: `query`, `period`, `source`, `problems_only`, `limit`, and `filters` as `{key, value}` entries. +- Sources: `external_api_log`, `whatsapp_webhook_event`, `flow_event`, `webhook_delivery`. Fallback path: -1. Message errors: `node scripts/errors.js` -2. API logs: `node scripts/api-logs.js` -3. Webhook deliveries: `node scripts/webhook-deliveries.js` +1. Unified log search: `node scripts/log-search.js --query "" --period 24h --limit 20` +2. Discover log-search filters and sources: `node scripts/log-search-catalog.js` +3. Message errors: `node scripts/errors.js` +4. API logs: `node scripts/api-logs.js` +5. Webhook deliveries: `node scripts/webhook-deliveries.js` + +Use direct API filters when you know the indexed field: +```bash +node scripts/log-search.js --source api --problems-only true --filter response_status=500 --filter endpoint_contains=/messages +node scripts/log-search.js --source workflows --filter flow_execution_id=exec_123 --limit 20 +``` ### Run health checks @@ -71,6 +88,8 @@ Fallback path: | Script | Purpose | |--------|---------| +| `log-search.js` | Search unified Logs V2 events across API, Meta, workflows, and webhook deliveries | +| `log-search-catalog.js` | List log-search sources, filters, and detail fields | | `errors.js` | List message errors | | `api-logs.js` | List external API logs | | `webhook-deliveries.js` | List webhook delivery attempts | @@ -95,20 +114,22 @@ npm i Examples: ```bash -node scripts/openapi-explore.mjs --spec platform search "webhook deliveries" -node scripts/openapi-explore.mjs --spec platform op listWebhookDeliveries -node scripts/openapi-explore.mjs --spec platform schema WebhookDelivery +node scripts/openapi-explore.mjs --spec platform search "log search" +node scripts/openapi-explore.mjs --spec platform op searchLogs +node scripts/openapi-explore.mjs --spec platform op getLogSearchCatalog ``` ## Notes - For webhook setup (create/update/delete, signature verification, event types), use `integrate-whatsapp`. - Prefer resolving a display phone number to the canonical `phone_number_id` before deep debugging. -- Keep the scripts as the fallback path when the CLI is unavailable or when you need API-log or webhook-delivery inspection. +- Prefer unified log search before older narrow tools when the user gives a request ID, WhatsApp `wamid.*`, endpoint, webhook ID, workflow execution ID, phone ID, conversation, or recent incident window. +- Keep the scripts as the fallback path when the CLI or MCP is unavailable. ## References - [references/message-debugging-reference.md](references/message-debugging-reference.md) - Message debugging guide +- [references/log-search-reference.md](references/log-search-reference.md) - Unified log search guide - [references/triage-reference.md](references/triage-reference.md) - Error triage guide - [references/health-reference.md](references/health-reference.md) - Health check guide @@ -122,8 +143,8 @@ node scripts/openapi-explore.mjs --spec platform schema WebhookDelivery [observe-whatsapp file map]|root: . |.:{package.json,SKILL.md} |assets:{health-example.json,message-debugging-example.json,triage-example.json} -|references:{health-reference.md,message-debugging-reference.md,triage-reference.md} -|scripts:{api-logs.js,errors.js,lookup-conversation.js,message-details.js,messages.js,openapi-explore.mjs,overview.js,webhook-deliveries.js,whatsapp-health.js} +|references:{health-reference.md,log-search-reference.md,message-debugging-reference.md,triage-reference.md} +|scripts:{api-logs.js,errors.js,log-search-catalog.js,log-search.js,lookup-conversation.js,message-details.js,messages.js,openapi-explore.mjs,overview.js,webhook-deliveries.js,whatsapp-health.js} |scripts/lib/messages:{args.js,kapso-api.js} |scripts/lib/status:{args.js,kapso-api.js} |scripts/lib/triage:{args.js,kapso-api.js} diff --git a/plugins/kapso/skills/observe-whatsapp/references/log-search-reference.md b/plugins/kapso/skills/observe-whatsapp/references/log-search-reference.md new file mode 100644 index 0000000..99aca4a --- /dev/null +++ b/plugins/kapso/skills/observe-whatsapp/references/log-search-reference.md @@ -0,0 +1,80 @@ +# Unified Log Search + +Use unified log search before narrow list endpoints when the user gives an identifier or incident window. It searches project-scoped API calls, Meta webhook events, workflow events, and outbound webhook deliveries. + +## Surfaces + +- CLI: `kapso logs search` +- MCP: `search_logs` +- Direct API fallback: `node scripts/log-search.js` +- Catalog fallback: `node scripts/log-search-catalog.js` + +## Sources + +- `external_api_log`: customer or backend calls into the Kapso Platform API +- `whatsapp_webhook_event`: raw Meta webhook event projections +- `flow_event`: workflow execution and step events +- `webhook_delivery`: Kapso webhook attempts to customer endpoints + +Aliases in the fallback script: +- `api` -> `external_api_log` +- `meta` -> `whatsapp_webhook_event` +- `workflows` -> `flow_event` +- `webhooks` -> `webhook_delivery` + +## Good Starting Searches + +Request or trace ID: +```bash +kapso logs search --query "req_123" --period 24h --limit 20 --output json +``` + +WhatsApp message ID: +```bash +kapso logs search --query "wamid.ABC123" --period 7d --output json +``` + +Failed API sends: +```bash +kapso logs search --source external_api_log --query "/messages" --problems-only --filter response_status=500 --output json +``` + +Workflow execution: +```bash +kapso logs search --source flow_event --filter flow_execution_id=exec_123 --limit 20 --output json +``` + +Direct API fallback: +```bash +node scripts/log-search.js --query "wamid.ABC123" --period 24h --limit 10 +node scripts/log-search.js --source webhooks --problems-only true --filter webhook_id=wh_123 +``` + +## Filters + +Use `node scripts/log-search-catalog.js` to discover available filter keys. Common useful filters include: + +- `request_id` +- `response_status` +- `endpoint_contains` +- `whatsapp_message_id` +- `phone_number_id` +- `flow_execution_id` +- `event_type` +- `webhook_id` +- `status` +- `has_status_error` + +Use filters only when you know the indexed field. Start with `query` when the identifier could appear in multiple sources. + +## Context Windows + +For event context around a timestamp, use the direct API fallback: +```bash +node scripts/log-search.js --period context --around "2026-07-07T12:00:00Z" --highlight-resource-id msg_123 --limit 30 +``` + +Use `pagination.next_cursor` from a previous response with: +```bash +node scripts/log-search.js --cursor "" --limit 50 +``` diff --git a/plugins/kapso/skills/observe-whatsapp/references/triage-reference.md b/plugins/kapso/skills/observe-whatsapp/references/triage-reference.md index 96f96fe..cba0190 100644 --- a/plugins/kapso/skills/observe-whatsapp/references/triage-reference.md +++ b/plugins/kapso/skills/observe-whatsapp/references/triage-reference.md @@ -14,12 +14,14 @@ ## Webhook delivery failures -1. Review recent delivery attempts. -2. Check response status codes and error messages. -3. Verify webhook URL availability and signature verification logic. +1. Start with unified log search for the webhook ID, URL text, event name, or recent problem events. +2. Review recent delivery attempts. +3. Check response status codes and error messages. +4. Verify webhook URL availability and signature verification logic. ## API errors -1. Review external API call logs. -2. Filter by status code or endpoint. -3. Identify auth errors, rate limits, or upstream failures. +1. Start with unified log search for the request ID, endpoint, phone ID, `wamid.*`, or response status. +2. Review external API call logs when you need the older narrow list view. +3. Filter by status code or endpoint. +4. Identify auth errors, rate limits, or upstream failures. diff --git a/plugins/kapso/skills/observe-whatsapp/scripts/log-search-catalog.js b/plugins/kapso/skills/observe-whatsapp/scripts/log-search-catalog.js new file mode 100644 index 0000000..711f321 --- /dev/null +++ b/plugins/kapso/skills/observe-whatsapp/scripts/log-search-catalog.js @@ -0,0 +1,36 @@ +const { kapsoConfigFromEnv, kapsoRequest } = require('./lib/triage/kapso-api'); + +function err(message, details) { + return { ok: false, error: { message, details } }; +} + +async function main() { + const argv = process.argv.slice(2); + if (argv.includes('--help') || argv.includes('-h')) { + console.log( + JSON.stringify( + { + ok: true, + usage: 'node scripts/log-search-catalog.js', + env: ['KAPSO_API_BASE_URL', 'KAPSO_API_KEY'] + }, + null, + 2 + ) + ); + return 0; + } + + try { + const config = kapsoConfigFromEnv(); + const data = await kapsoRequest(config, '/platform/v1/log_search/catalog'); + console.log(JSON.stringify({ ok: true, data }, null, 2)); + return 0; + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + console.error(JSON.stringify(err('Command failed', { message }), null, 2)); + return 1; + } +} + +main().then((code) => process.exit(code)); diff --git a/plugins/kapso/skills/observe-whatsapp/scripts/log-search.js b/plugins/kapso/skills/observe-whatsapp/scripts/log-search.js new file mode 100644 index 0000000..8a5e46f --- /dev/null +++ b/plugins/kapso/skills/observe-whatsapp/scripts/log-search.js @@ -0,0 +1,299 @@ +const { kapsoConfigFromEnv, kapsoRequest } = require('./lib/triage/kapso-api'); + +const PERIODS = new Set(['24h', '7d', '30d', 'context']); +const SOURCE_ALIASES = new Map([ + ['all', 'all'], + ['api', 'external_api_log'], + ['external_api_log', 'external_api_log'], + ['flow', 'flow_event'], + ['flows', 'flow_event'], + ['workflow', 'flow_event'], + ['workflows', 'flow_event'], + ['flow_event', 'flow_event'], + ['meta', 'whatsapp_webhook_event'], + ['whatsapp_webhook_event', 'whatsapp_webhook_event'], + ['webhook', 'webhook_delivery'], + ['webhooks', 'webhook_delivery'], + ['webhook_delivery', 'webhook_delivery'] +]); + +function ok(data) { + return { ok: true, data }; +} + +function err(message, details) { + return { ok: false, error: { message, details } }; +} + +async function main() { + const argv = process.argv.slice(2); + if (hasHelpFlag(argv)) { + console.log( + JSON.stringify( + { + ok: true, + usage: + 'node scripts/log-search.js [--query ] [--period <24h|7d|30d|context>] [--source ] [--problems-only true|false] [--limit ] [--cursor ] [--around ] [--highlight-event-id ] [--highlight-resource-id ] [--filter ...] [--filters-json ]', + notes: [ + 'Uses GET /platform/v1/log_search when no filters are provided.', + 'Uses POST /platform/v1/log_search when --filter or --filters-json is provided.', + 'Use --period context with --around to fetch nearby events.' + ], + examples: [ + 'node scripts/log-search.js --query wamid.ABC123 --period 24h --limit 10', + 'node scripts/log-search.js --source api --problems-only true --filter response_status=500 --filter endpoint_contains=/messages', + 'node scripts/log-search.js --source workflows --filter flow_execution_id=exec_123 --limit 20' + ], + env: ['KAPSO_API_BASE_URL', 'KAPSO_API_KEY'] + }, + null, + 2 + ) + ); + return 0; + } + + try { + const flags = parseFlags(argv); + const request = buildSearchRequest(flags); + const config = kapsoConfigFromEnv(); + const data = await executeSearch(config, request); + + console.log(JSON.stringify(ok(data), null, 2)); + return 0; + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + console.error(JSON.stringify(err('Command failed', { message }), null, 2)); + return 1; + } +} + +function hasHelpFlag(argv) { + return argv.includes('--help') || argv.includes('-h'); +} + +function parseFlags(argv) { + const flags = {}; + const filters = []; + + for (let index = 0; index < argv.length; index += 1) { + const arg = argv[index]; + if (!arg.startsWith('--')) { + throw new Error(`Unexpected positional argument: ${arg}`); + } + + const trimmed = arg.slice(2); + const eqIndex = trimmed.indexOf('='); + const key = eqIndex >= 0 ? trimmed.slice(0, eqIndex) : trimmed; + let value; + + if (eqIndex >= 0) { + value = trimmed.slice(eqIndex + 1); + } else { + const next = argv[index + 1]; + if (!next || next.startsWith('--')) { + value = true; + } else { + value = next; + index += 1; + } + } + + if (key === 'filter') { + filters.push(requiredValue(key, value)); + } else if (Object.hasOwn(flags, key)) { + throw new Error(`Duplicate --${key} flag`); + } else { + flags[key] = value; + } + } + + if (filters.length > 0) { + flags.filter = filters; + } + + return flags; +} + +function buildSearchRequest(flags) { + const filters = filtersFromFlags(flags); + const body = {}; + + setString(body, 'query', flags.query); + setPeriod(body, flags.period); + setSource(body, flags.source); + setBoolean(body, 'problems_only', flags['problems-only'] ?? flags['errors-only']); + setInteger(body, 'limit', flags.limit); + setString(body, 'cursor', flags.cursor); + setString(body, 'around', flags.around); + setString(body, 'highlight_event_id', flags['highlight-event-id']); + setString(body, 'highlight_resource_id', flags['highlight-resource-id']); + + if (Object.keys(filters).length > 0) { + body.filters = filters; + } + + return { + body, + hasFilters: Object.hasOwn(body, 'filters') + }; +} + +function filtersFromFlags(flags) { + const filters = {}; + + if (flags['filters-json'] !== undefined) { + const parsed = parseFiltersJson(requiredValue('filters-json', flags['filters-json'])); + Object.assign(filters, parsed); + } + + for (const entry of flags.filter || []) { + const separatorIndex = entry.indexOf('='); + if (separatorIndex <= 0) { + throw new Error(`Invalid filter "${entry}". Use "--filter key=value".`); + } + + const key = entry.slice(0, separatorIndex).trim(); + const value = entry.slice(separatorIndex + 1).trim(); + if (!key || !value) { + throw new Error(`Invalid filter "${entry}". Use "--filter key=value".`); + } + if (Object.hasOwn(filters, key)) { + throw new Error(`Duplicate filter "${key}". Pass each filter key only once.`); + } + + filters[key] = parseFilterValue(value); + } + + return filters; +} + +function parseFiltersJson(value) { + let parsed; + try { + parsed = JSON.parse(value); + } catch (error) { + throw new Error(`Invalid --filters-json value: ${error.message}`); + } + + if (Array.isArray(parsed)) { + return parsed.reduce((filters, entry) => { + if (!entry || typeof entry !== 'object' || Array.isArray(entry)) { + throw new Error('--filters-json array entries must be objects with key and value'); + } + if (!Object.hasOwn(entry, 'key') || !Object.hasOwn(entry, 'value')) { + throw new Error('--filters-json array entries must include key and value'); + } + filters[String(entry.key)] = parseFilterValue(entry.value); + return filters; + }, {}); + } + + if (!parsed || typeof parsed !== 'object') { + throw new Error('--filters-json must be an object or array of {key, value} entries'); + } + + return Object.entries(parsed).reduce((filters, [key, value]) => { + filters[key] = parseFilterValue(value); + return filters; + }, {}); +} + +function parseFilterValue(value) { + if (typeof value === 'boolean' || typeof value === 'number') { + return value; + } + + const stringValue = String(value).trim(); + if (/^-?\d+$/.test(stringValue)) { + return Number.parseInt(stringValue, 10); + } + if (stringValue === 'true') { + return true; + } + if (stringValue === 'false') { + return false; + } + + return stringValue; +} + +function setString(body, key, rawValue) { + if (rawValue === undefined) return; + body[key] = requiredValue(key.replaceAll('_', '-'), rawValue); +} + +function setPeriod(body, rawValue) { + if (rawValue === undefined) return; + const period = requiredValue('period', rawValue); + if (!PERIODS.has(period)) { + throw new Error(`Invalid --period value: ${period}`); + } + body.period = period; +} + +function setSource(body, rawValue) { + if (rawValue === undefined) return; + const source = requiredValue('source', rawValue).toLowerCase(); + const normalized = SOURCE_ALIASES.get(source); + if (!normalized) { + throw new Error(`Invalid --source value: ${source}`); + } + body.source = normalized; +} + +function setBoolean(body, key, rawValue) { + if (rawValue === undefined) return; + if (rawValue === true) { + body[key] = true; + return; + } + + const value = String(rawValue).toLowerCase(); + if (['true', '1', 'yes'].includes(value)) { + body[key] = true; + return; + } + if (['false', '0', 'no'].includes(value)) { + body[key] = false; + return; + } + + throw new Error(`Invalid boolean for --${key.replaceAll('_', '-')}: ${rawValue}`); +} + +function setInteger(body, key, rawValue) { + if (rawValue === undefined) return; + const value = requiredValue(key, rawValue); + const parsed = Number.parseInt(value, 10); + if (!Number.isInteger(parsed) || parsed <= 0 || String(parsed) !== value) { + throw new Error(`Invalid --${key} value: ${rawValue}`); + } + body[key] = parsed; +} + +function requiredValue(key, value) { + if (value === true || value === undefined || value === '') { + throw new Error(`--${key} requires a value`); + } + return String(value); +} + +async function executeSearch(config, request) { + if (request.hasFilters) { + return kapsoRequest(config, '/platform/v1/log_search', { + method: 'POST', + body: JSON.stringify(request.body) + }); + } + + const params = new URLSearchParams(); + for (const [key, value] of Object.entries(request.body)) { + params.set(key, String(value)); + } + + const suffix = params.toString(); + return kapsoRequest(config, `/platform/v1/log_search${suffix ? `?${suffix}` : ''}`); +} + +main().then((code) => process.exit(code)); From 17d7a94a8cbdc22180ec4827c52522b91f5604d5 Mon Sep 17 00:00:00 2001 From: Gaonuk Date: Tue, 7 Jul 2026 13:17:45 -0400 Subject: [PATCH 2/2] docs: remove v2 log search wording --- plugins/kapso/CHANGELOG.md | 2 +- plugins/kapso/skills/observe-whatsapp/SKILL.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/kapso/CHANGELOG.md b/plugins/kapso/CHANGELOG.md index 5c1d30c..d63fd01 100644 --- a/plugins/kapso/CHANGELOG.md +++ b/plugins/kapso/CHANGELOG.md @@ -2,7 +2,7 @@ ## Unreleased -- Added unified project log search guidance and fallback scripts for Logs V2. +- Added unified project log search guidance and fallback scripts. ## 0.1.0 diff --git a/plugins/kapso/skills/observe-whatsapp/SKILL.md b/plugins/kapso/skills/observe-whatsapp/SKILL.md index b5124ca..07889e9 100644 --- a/plugins/kapso/skills/observe-whatsapp/SKILL.md +++ b/plugins/kapso/skills/observe-whatsapp/SKILL.md @@ -88,7 +88,7 @@ Fallback path: | Script | Purpose | |--------|---------| -| `log-search.js` | Search unified Logs V2 events across API, Meta, workflows, and webhook deliveries | +| `log-search.js` | Search unified log events across API, Meta, workflows, and webhook deliveries | | `log-search-catalog.js` | List log-search sources, filters, and detail fields | | `errors.js` | List message errors | | `api-logs.js` | List external API logs |