diff --git a/plugins/kapso/skills/automate-whatsapp/SKILL.md b/plugins/kapso/skills/automate-whatsapp/SKILL.md index 6256cf6..b6a03e0 100644 --- a/plugins/kapso/skills/automate-whatsapp/SKILL.md +++ b/plugins/kapso/skills/automate-whatsapp/SKILL.md @@ -1,13 +1,13 @@ --- name: automate-whatsapp -description: "Build WhatsApp automations with Kapso workflows: configure WhatsApp triggers, edit workflow graphs, manage executions, deploy functions, and use databases/integrations for state. Use when automating WhatsApp conversations and event handling." +description: "Build WhatsApp automations with Kapso workflows: configure WhatsApp triggers, edit workflow graphs, manage executions, deploy functions, and debug automation behavior. Use when automating WhatsApp conversations and event handling." --- # Automate WhatsApp ## When to use -Use this skill to build and run WhatsApp automations: workflow CRUD, graph edits, triggers, executions, function management, app integrations, and D1 database operations. +Use this skill to build and run WhatsApp automations: workflow CRUD, graph edits, triggers, executions, function management, webhook tools, and MCP tools. ## Setup @@ -124,14 +124,6 @@ For inbound_message triggers, prefer `kapso whatsapp numbers resolve --phone-num Use `--public-endpoint true` when the function should be callable without `X-API-Key` via the Kapso-hosted invoke URL. This is only supported for Cloudflare functions. New functions default to `invoke_response_mode=passthrough`, which returns the function body directly on successful invoke. Legacy wrapped functions can be migrated later with `update-function.js`. -### Set up agent node with app integrations - -1. Find model: `node scripts/list-provider-models.js` -2. Find account: `node scripts/list-accounts.js --app-slug ` (use `pipedream_account_id`) -3. Find action: `node scripts/search-actions.js --query --app-slug ` (action_id = key) -4. Create integration: `node scripts/create-integration.js --action-id --app-slug --account-id --configured-props ` -5. Add tools to agent node via `flow_agent_app_integration_tools` - ### Set up agent node with remote sandbox repositories Use this when the agent needs a remote ephemeral workspace to inspect or modify repository files during a workflow run. @@ -159,12 +151,6 @@ Notes: - Repositories are mounted into `/workspace/repos/` inside the remote sandbox - Use `references/agent-remote-sandbox.md` and `references/node-types.md` for the exact shape -### Database CRUD - -1. List tables: `node scripts/list-tables.js` -2. Query: `node scripts/query-rows.js --table --filters ` -3. Create/update/delete with row scripts - ## Graph rules - Exactly one start node with `id` = `start` @@ -182,7 +168,7 @@ For full schema details, see `references/graph-contract.md`. async function handler(request, env) { // Parse input const body = await request.json(); - // Use env.KV and env.DB as needed + // Use env.KV and secrets as needed return new Response(JSON.stringify({ result: "ok" })); } ``` @@ -251,34 +237,6 @@ Always use this structure: | `invoke-function.js` | Invoke function with payload | | `list-function-invocations.js` | List function invocations | -### App integrations - -| Script | Purpose | -|--------|---------| -| `list-apps.js` | Search integration apps | -| `search-actions.js` | Search actions (action_id = key) | -| `get-action-schema.js` | Get action JSON schema | -| `list-accounts.js` | List connected accounts | -| `create-connect-token.js` | Create OAuth connect link | -| `configure-prop.js` | Resolve remote_options for a prop | -| `reload-props.js` | Reload dynamic props | -| `list-integrations.js` | List saved integrations | -| `create-integration.js` | Create an integration | -| `update-integration.js` | Update an integration | -| `delete-integration.js` | Delete an integration | - -### Databases - -| Script | Purpose | -|--------|---------| -| `list-tables.js` | List D1 tables | -| `get-table.js` | Get table schema + sample rows | -| `query-rows.js` | Query rows with filters | -| `create-row.js` | Create a row | -| `update-row.js` | Update rows | -| `upsert-row.js` | Upsert a row | -| `delete-row.js` | Delete rows | - ### OpenAPI | Script | Purpose | @@ -294,16 +252,12 @@ Examples: ```bash node scripts/openapi-explore.mjs --spec workflows search "variables" node scripts/openapi-explore.mjs --spec workflows op getWorkflowVariables -node scripts/openapi-explore.mjs --spec platform op queryDatabaseRows ``` ## Notes - Prefer file paths over inline JSON (`--definition-file`, `--code-file`) -- `action_id` is the same as `key` from `search-actions` -- `--account-id` uses `pipedream_account_id` from `list-accounts` - Variable CRUD (`variables-set.js`, `variables-delete.js`) is blocked - Platform API doesn't support it -- Raw SQL execution is not supported via Platform API ## References @@ -316,11 +270,9 @@ Read before editing: Other references: - [references/execution-context.md](references/execution-context.md) - Context structure and variable substitution - [references/triggers.md](references/triggers.md) - Trigger types and setup -- [references/app-integrations.md](references/app-integrations.md) - App integration and variable_definitions - [references/agent-remote-sandbox.md](references/agent-remote-sandbox.md) - Remote sandbox behavior, repo resources, mounted paths - [references/functions-reference.md](references/functions-reference.md) - Function management - [references/functions-payloads.md](references/functions-payloads.md) - Payload shapes for functions -- [references/databases-reference.md](references/databases-reference.md) - Database operations ## Assets @@ -334,7 +286,6 @@ Other references: | `workflow-interactive-buttons-decide-ai.json` | Interactive buttons + decide (AI) | | `workflow-api-template-wait-agent.json` | API trigger + template + agent | | `function-decide-route-interactive-buttons.json` | Function for button routing | -| `agent-app-integration-example.json` | Agent node with app integrations | | `agent-remote-sandbox-github-repo-example.json` | Agent node with remote sandbox + GitHub repo resource | ## Related skills @@ -346,10 +297,9 @@ Other references: ```text [automate-whatsapp file map]|root: . |.:{package.json,SKILL.md} -|assets:{agent-app-integration-example.json,agent-remote-sandbox-github-repo-example.json,databases-example.json,function-decide-route-interactive-buttons.json,functions-example.json,workflow-agent-simple.json,workflow-api-template-wait-agent.json,workflow-customer-support-intake-agent.json,workflow-decision.json,workflow-interactive-buttons-decide-ai.json,workflow-interactive-buttons-decide-function.json,workflow-linear.json} -|references:{agent-remote-sandbox.md,app-integrations.md,databases-reference.md,execution-context.md,function-contracts.md,functions-payloads.md,functions-reference.md,graph-contract.md,local-workflow-source.md,node-types.md,triggers.md,workflow-overview.md,workflow-reference.md} -|scripts:{configure-prop.js,create-connect-token.js,create-function.js,create-integration.js,create-row.js,create-trigger.js,create-workflow.js,delete-integration.js,delete-row.js,delete-trigger.js,deploy-function.js,edit-graph.js,get-action-schema.js,get-context-value.js,get-execution-event.js,get-execution.js,get-function.js,get-graph.js,get-table.js,get-workflow.js,invoke-function.js,list-accounts.js,list-apps.js,list-execution-events.js,list-executions.js,list-function-invocations.js,list-functions.js,list-integrations.js,list-provider-models.js,list-tables.js,list-triggers.js,list-whatsapp-phone-numbers.js,list-workflows.js,openapi-explore.mjs,query-rows.js,reload-props.js,resume-execution.js,search-actions.js,update-execution-status.js,update-function.js,update-graph.js,update-integration.js,update-row.js,update-trigger.js,update-workflow-settings.js,upsert-row.js,validate-graph.js,variables-delete.js,variables-list.js,variables-set.js} -|scripts/lib/databases:{args.js,filters.js,kapso-api.js} +|assets:{agent-remote-sandbox-github-repo-example.json,function-decide-route-interactive-buttons.json,functions-example.json,workflow-agent-simple.json,workflow-api-template-wait-agent.json,workflow-customer-support-intake-agent.json,workflow-decision.json,workflow-interactive-buttons-decide-ai.json,workflow-interactive-buttons-decide-function.json,workflow-linear.json} +|references:{agent-remote-sandbox.md,execution-context.md,function-contracts.md,functions-payloads.md,functions-reference.md,graph-contract.md,local-workflow-source.md,node-types.md,triggers.md,workflow-overview.md,workflow-reference.md} +|scripts:{create-function.js,create-trigger.js,create-workflow.js,delete-trigger.js,deploy-function.js,edit-graph.js,get-context-value.js,get-execution-event.js,get-execution.js,get-function.js,get-graph.js,get-workflow.js,invoke-function.js,list-execution-events.js,list-executions.js,list-function-invocations.js,list-functions.js,list-provider-models.js,list-triggers.js,list-whatsapp-phone-numbers.js,list-workflows.js,openapi-explore.mjs,resume-execution.js,update-execution-status.js,update-function.js,update-graph.js,update-trigger.js,update-workflow-settings.js,validate-graph.js,variables-delete.js,variables-list.js,variables-set.js} |scripts/lib/functions:{args.js,kapso-api.js} |scripts/lib/workflows:{args.js,kapso-api.js,result.js} ``` diff --git a/plugins/kapso/skills/automate-whatsapp/assets/agent-app-integration-example.json b/plugins/kapso/skills/automate-whatsapp/assets/agent-app-integration-example.json deleted file mode 100644 index 70a7aad..0000000 --- a/plugins/kapso/skills/automate-whatsapp/assets/agent-app-integration-example.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "integration_payload": { - "action_id": "google_calendar-query-free-busy-calendars", - "app_slug": "google_calendar", - "account_id": "apn_example", - "configured_props": { - "calendarId": "{{calendar_id}}", - "timeMin": "{{time_min}}", - "timeMax": "{{time_max}}" - }, - "variable_definitions": { - "calendar_id": "string", - "time_min": "string", - "time_max": "string" - } - }, - "agent_node_config": { - "node_type": "agent", - "config": { - "system_prompt": "Check calendar availability and book appointments.", - "provider_model_id": "uuid", - "max_iterations": 10, - "temperature": 0.7, - "flow_agent_app_integration_tools": [ - { - "name": "check_calendar", - "description": "Check availability in Google Calendar", - "app_integration_id": "integration_uuid" - } - ], - "flow_agent_webhooks": [], - "flow_agent_mcp_servers": [] - } - } -} diff --git a/plugins/kapso/skills/automate-whatsapp/assets/agent-remote-sandbox-github-repo-example.json b/plugins/kapso/skills/automate-whatsapp/assets/agent-remote-sandbox-github-repo-example.json index 536562f..746e79d 100644 --- a/plugins/kapso/skills/automate-whatsapp/assets/agent-remote-sandbox-github-repo-example.json +++ b/plugins/kapso/skills/automate-whatsapp/assets/agent-remote-sandbox-github-repo-example.json @@ -22,7 +22,6 @@ ], "flow_agent_webhooks": [], "flow_agent_function_tools": [], - "flow_agent_app_integration_tools": [], "flow_agent_mcp_servers": [] } } diff --git a/plugins/kapso/skills/automate-whatsapp/assets/databases-example.json b/plugins/kapso/skills/automate-whatsapp/assets/databases-example.json deleted file mode 100644 index 984e335..0000000 --- a/plugins/kapso/skills/automate-whatsapp/assets/databases-example.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "filters": { - "status": "eq.active", - "created_at": "gte.2025-01-01" - }, - "data": { - "phone": "+14155550123", - "name": "Alicia", - "status": "active" - } -} diff --git a/plugins/kapso/skills/automate-whatsapp/assets/functions-example.json b/plugins/kapso/skills/automate-whatsapp/assets/functions-example.json index 4dd7d11..96245d4 100644 --- a/plugins/kapso/skills/automate-whatsapp/assets/functions-example.json +++ b/plugins/kapso/skills/automate-whatsapp/assets/functions-example.json @@ -1,5 +1,5 @@ { "name": "webhook-handler", "description": "Store webhook payloads", - "code": "async function handler(request, env) {\n const payload = await request.json();\n await env.DB.prepare(\"INSERT INTO webhook_logs (event, data) VALUES (?, ?)\").bind(payload.event, JSON.stringify(payload.data)).run();\n return new Response(JSON.stringify({ success: true }), { headers: { 'Content-Type': 'application/json' } });\n}" + "code": "async function handler(request, env) {\n const payload = await request.json();\n const key = `webhook:${Date.now()}`;\n await env.KV.put(key, JSON.stringify({ event: payload.event, data: payload.data }));\n return new Response(JSON.stringify({ success: true }), { headers: { 'Content-Type': 'application/json' } });\n}" } diff --git a/plugins/kapso/skills/automate-whatsapp/references/app-integrations.md b/plugins/kapso/skills/automate-whatsapp/references/app-integrations.md deleted file mode 100644 index af2101f..0000000 --- a/plugins/kapso/skills/automate-whatsapp/references/app-integrations.md +++ /dev/null @@ -1,89 +0,0 @@ -# App Integrations (Workflow Nodes and Agent Tools) - -Use these when you need to call external apps (Slack, HubSpot, Sheets, etc). - -## Step 1: Accounts - -1. List connected accounts: - - `scripts/list-accounts.js --app-slug ` - - Use `accounts[].pipedream_account_id` for any `--account-id` flag. -2. If no account exists, generate a connect link: - - `scripts/create-connect-token.js --app-slug ` -3. Ask the user to open the connect URL and finish OAuth, then re-run list-accounts. - -## Action discovery notes - -- `action_id` is the same as the action `key` returned by `search-actions`. -- Prefer one-word queries (ex: `calendar`, `slack`, `hubspot`) and then filter by `app_slug`. -- If a multi-word query returns nothing, retry with a single token. - -## Inputs and variable_definitions (required for agent tools) - -App integration tools only accept inputs defined by `variable_definitions`. These inputs are mapped into -`configured_props` using `{{placeholders}}`. - -Rules: -- Every placeholder in `configured_props` becomes a required tool input. -- If `variable_definitions` is omitted, Kapso infers variable names from `{{placeholders}}` - and treats them as `string`. -- Agent tool calls must pass an `input` object with these variables. - -Example configured props: -```json -{ - "calendarId": "{{calendar_id}}", - "timeMin": "{{time_min}}", - "timeMax": "{{time_max}}" -} -``` - -Example variable definitions: -```json -{ - "calendar_id": "string", - "time_min": "string", - "time_max": "string" -} -``` - -Create integration with explicit variable definitions: -```bash -node scripts/create-integration.js \ - --action-id "google_calendar-query-free-busy-calendars" \ - --app-slug "google_calendar" \ - --account-id "" \ - --configured-props '{"calendarId":"{{calendar_id}}","timeMin":"{{time_min}}","timeMax":"{{time_max}}"}' \ - --variable-definitions '{"calendar_id":"string","time_min":"string","time_max":"string"}' -``` - -Example asset: `assets/agent-app-integration-example.json` - -## Step 2: Choose integration path - -### Option A: Pipedream node (workflow graph) - -1. Search actions: `scripts/search-actions.js --query "slack" --app-slug slack` -2. Get schema: `scripts/get-action-schema.js --action-id ` -3. For remote options: `scripts/configure-prop.js --action-id --prop-name --account-id ` -4. For dynamic props: `scripts/reload-props.js --action-id --account-id ` -5. Add a `pipedream` node to the graph with action_id, app_slug, account_id, configured_props. - -### Option B: Agent app integration tool (preferred for agent nodes) - -1. List existing integrations: `scripts/list-integrations.js` -2. If none, create one: - - `scripts/create-integration.js --action-id --app-slug --account-id --configured-props ` -3. Use the integration id in `flow_agent_app_integration_tools` on the agent node. - -### Option C: Integration via webhook - -Use only when calling from a webhook node or agent webhook tool. - -1. Create integration (same as Option B). -2. Call: - - `https://app.kapso.ai/api/v1/integrations/{integration_id}/invoke` - -Rules: -- Prefer Option B for agent nodes. -- Pipedream URLs do not work in webhook nodes or agent webhook tools. -- Always get action_id from search-actions; do not guess. diff --git a/plugins/kapso/skills/automate-whatsapp/references/databases-reference.md b/plugins/kapso/skills/automate-whatsapp/references/databases-reference.md deleted file mode 100644 index b361fcf..0000000 --- a/plugins/kapso/skills/automate-whatsapp/references/databases-reference.md +++ /dev/null @@ -1,21 +0,0 @@ -# Database Filters Reference - -Use PostgREST-style filters as query parameters. - -## Operators - -- `eq`: equals (`status=eq.active`) -- `neq`: not equal (`status=neq.inactive`) -- `gt`: greater than (`age=gt.18`) -- `gte`: greater or equal (`age=gte.18`) -- `lt`: less than (`price=lt.100`) -- `lte`: less or equal (`price=lte.100`) -- `like`: pattern match (`name=like.%john%`) -- `in`: list match (`status=in.(active,pending)`) -- `is.null`: is null (`deleted_at=is.null`) - -## Common patterns - -- Select columns: `?select=id,name,email` -- Order: `?order=created_at.desc` -- Pagination: `?limit=20&offset=40` diff --git a/plugins/kapso/skills/automate-whatsapp/references/functions-reference.md b/plugins/kapso/skills/automate-whatsapp/references/functions-reference.md index bbc34a9..9dd3d0f 100644 --- a/plugins/kapso/skills/automate-whatsapp/references/functions-reference.md +++ b/plugins/kapso/skills/automate-whatsapp/references/functions-reference.md @@ -13,7 +13,6 @@ Do not use `export` or arrow functions. Return a `Response` object. ## Runtime APIs - `request`: Fetch API Request; use `await request.json()` for JSON. -- `env.DB`: D1 database access (if enabled for the project). - `env.KV`: KV storage with `.get(key)`, `.put(key, value)`, `.delete(key)`. - `env.SECRET_NAME`: Secrets configured in the function settings. diff --git a/plugins/kapso/skills/automate-whatsapp/references/local-workflow-source.md b/plugins/kapso/skills/automate-whatsapp/references/local-workflow-source.md index be05895..c5b14eb 100644 --- a/plugins/kapso/skills/automate-whatsapp/references/local-workflow-source.md +++ b/plugins/kapso/skills/automate-whatsapp/references/local-workflow-source.md @@ -89,4 +89,4 @@ Use `scripts/get-graph.js`, `scripts/update-graph.js`, and related Platform API - the user cannot use the local CLI source-sync repo, - you need direct graph inspection for debugging, - a task is not yet supported by `kapso pull/build/push`, -- or you need one-off API operations such as execution inspection or app integration setup. +- or you need one-off API operations such as execution inspection. diff --git a/plugins/kapso/skills/automate-whatsapp/references/node-types.md b/plugins/kapso/skills/automate-whatsapp/references/node-types.md index 802f6c4..2a768b8 100644 --- a/plugins/kapso/skills/automate-whatsapp/references/node-types.md +++ b/plugins/kapso/skills/automate-whatsapp/references/node-types.md @@ -13,7 +13,6 @@ These are the `data.node_type` values supported by the Platform API and validate - `decide` - `call` - `webhook` -- `pipedream` - `function` - `agent` - `handoff` @@ -222,29 +221,6 @@ Rules: `headers` and `body_template` must be valid JSON objects. -## pipedream (apps) - -```json -{ - "node_type": "pipedream", - "config": { - "action_id": "slack-send_message_to_channel", - "app_slug": "slack", - "account_id": "apn_example", - "configured_props": { - "channel": "#general", - "text": "Hello {{vars.user_name}}!" - }, - "dynamic_props_id": "dp_optional", - "save_response_to": "app_result" - } -} -``` - -Notes: -- Use `references/app-integrations.md` for how to find `action_id`, `account_id`, and build `configured_props`. -- `configured_props` may include `{{vars.*}}`, `{{system.*}}`, `{{context.*}}` runtime variables. - ## function ```json @@ -284,7 +260,6 @@ Default tools (toggle on/off only): - ask_about_file Custom tools: -- `flow_agent_app_integration_tools[]` (app integrations) - `flow_agent_webhooks[]` (webhook tools) - `flow_agent_mcp_servers[]` (MCP tools) @@ -304,7 +279,6 @@ All tool arrays go under `data.config` of the agent node: "provider_model_id": "uuid", "max_iterations": 10, "temperature": 0.7, - "flow_agent_app_integration_tools": [], "flow_agent_webhooks": [], "flow_agent_mcp_servers": [] } @@ -312,40 +286,6 @@ All tool arrays go under `data.config` of the agent node: } ``` -Example asset: `assets/agent-app-integration-example.json` - -#### App integration tools (preferred for agent nodes) - -Use pre-configured integrations and attach them as tools: - -```json -{ - "flow_agent_app_integration_tools": [ - { - "name": "check_calendar", - "description": "Check availability in Google Calendar", - "app_integration_id": "integration_uuid" - } - ] -} -``` - -Rules: -- `app_integration_id` is the integration UUID from `scripts/list-integrations.js`. -- Do not include headers or URLs here; the integration handles auth. -- Inputs are defined by the integration's `variable_definitions` (or `{{placeholders}}`). - -Tool input payload (runtime): -```json -{ - "input": { - "calendar_id": "primary", - "time_min": "2025-01-01T10:00:00Z", - "time_max": "2025-01-01T12:00:00Z" - } -} -``` - #### Webhook tools (custom HTTP tools) Use when the agent needs to call arbitrary HTTP endpoints: diff --git a/plugins/kapso/skills/automate-whatsapp/references/workflow-reference.md b/plugins/kapso/skills/automate-whatsapp/references/workflow-reference.md index bdb5dda..a6f804d 100644 --- a/plugins/kapso/skills/automate-whatsapp/references/workflow-reference.md +++ b/plugins/kapso/skills/automate-whatsapp/references/workflow-reference.md @@ -2,7 +2,7 @@ ## Overview -This skill manages workflow graphs, triggers, executions, and app integrations. Prefer the Kapso CLI local source workflow for normal workflow/function edits; use these Platform API scripts as fallback tools for debugging, unsupported CLI tasks, and API-only environments. Variables CRUD is not supported and will return blocked responses. +This skill manages workflow graphs, triggers, executions, and functions. Prefer the Kapso CLI local source workflow for normal workflow/function edits; use these Platform API scripts as fallback tools for debugging, unsupported CLI tasks, and API-only environments. Variables CRUD is not supported and will return blocked responses. For the CLI source-sync workflow and `@kapso/workflows`, read `references/local-workflow-source.md`. @@ -40,17 +40,6 @@ Each script is a single operation. Run with `node` or `bun`. - `scripts/list-provider-models.js` - `scripts/list-execution-events.js` - `scripts/get-execution-event.js` -- `scripts/list-apps.js` -- `scripts/search-actions.js` -- `scripts/get-action-schema.js` -- `scripts/list-accounts.js` -- `scripts/create-connect-token.js` -- `scripts/configure-prop.js` -- `scripts/reload-props.js` -- `scripts/list-integrations.js` -- `scripts/create-integration.js` -- `scripts/update-integration.js` -- `scripts/delete-integration.js` - `scripts/list-whatsapp-phone-numbers.js` ## Platform API endpoints @@ -74,17 +63,6 @@ Implemented calls: - `GET /platform/v1/workflow_executions/:id/events` - `GET /platform/v1/workflow_events/:id` - `GET /platform/v1/provider_models` -- `GET /platform/v1/integrations` -- `POST /platform/v1/integrations` -- `PATCH /platform/v1/integrations/:id` -- `DELETE /platform/v1/integrations/:id` -- `GET /platform/v1/integrations/apps` -- `GET /platform/v1/integrations/actions` -- `GET /platform/v1/integrations/accounts` -- `POST /platform/v1/integrations/connect_token` -- `GET /platform/v1/integrations/actions/:action_id/schema` -- `POST /platform/v1/integrations/actions/:action_id/configure_prop` -- `POST /platform/v1/integrations/actions/:action_id/reload_props` - `GET /platform/v1/whatsapp/phone_numbers` (for inbound_message triggers) Variables CRUD endpoints are not defined for Platform API. Scripts intentionally return blocked for create/update/delete operations. diff --git a/plugins/kapso/skills/automate-whatsapp/scripts/configure-prop.js b/plugins/kapso/skills/automate-whatsapp/scripts/configure-prop.js deleted file mode 100644 index 3fc13cd..0000000 --- a/plugins/kapso/skills/automate-whatsapp/scripts/configure-prop.js +++ /dev/null @@ -1,113 +0,0 @@ -#!/usr/bin/env node -import { loadConfig, requestJson } from './lib/workflows/kapso-api.js'; -import { ok, err, printJson } from './lib/workflows/result.js'; -import { parseArgs, getFlag, getBooleanFlag } from './lib/workflows/args.js'; - -function usage() { - return ok({ - usage: 'node scripts/configure-prop.js --action-id --prop-name --account-id [--configured-props ] [--dynamic-props-id ]', - notes: [ - 'Use accounts[].pipedream_account_id for --account-id.', - 'If you pass an internal account UUID, the script will try to resolve it.' - ], - env: ['KAPSO_API_BASE_URL', 'KAPSO_API_KEY'] - }); -} - -function parseJson(value, label) { - if (!value) return undefined; - try { - return JSON.parse(value); - } catch (error) { - throw new Error(`Invalid JSON for ${label}: ${String(error?.message || error)}`); - } -} - -function normalizeAccounts(payload) { - if (Array.isArray(payload?.accounts)) return payload.accounts; - if (Array.isArray(payload?.accounts?.accounts)) return payload.accounts.accounts; - if (Array.isArray(payload)) return payload; - return []; -} - -async function resolveAccountId(config, accountId) { - if (accountId.startsWith('apn_')) return accountId; - - const response = await requestJson(config, { - method: 'GET', - path: '/platform/v1/integrations/accounts' - }); - - if (!response.ok) { - throw new Error('Unable to resolve account id. Use list-accounts and pass pipedream_account_id.'); - } - - const accounts = normalizeAccounts(response.data); - const match = accounts.find((account) => account.id === accountId); - if (match?.pipedream_account_id) return match.pipedream_account_id; - - throw new Error('account-id must be pipedream_account_id (use list-accounts output).'); -} - -async function main() { - const parsed = parseArgs(process.argv.slice(2)); - if (getBooleanFlag(parsed.flags, 'help') || getBooleanFlag(parsed.flags, 'h')) { - printJson(usage()); - return 0; - } - - const actionId = getFlag(parsed.flags, 'action-id'); - const propName = getFlag(parsed.flags, 'prop-name'); - const accountId = getFlag(parsed.flags, 'account-id'); - - if (!actionId || !propName || !accountId) { - printJson(err('action-id, prop-name, and account-id are required')); - return 2; - } - - let configuredProps = {}; - try { - const provided = parseJson(getFlag(parsed.flags, 'configured-props'), 'configured-props'); - configuredProps = provided || {}; - } catch (error) { - printJson(err('Failed to parse configured-props', { message: error.message })); - return 2; - } - - const config = loadConfig(); - let resolvedAccountId = accountId; - - try { - resolvedAccountId = await resolveAccountId(config, accountId); - } catch (error) { - printJson(err('Failed to resolve account-id', { message: error.message })); - return 2; - } - - if (!Object.keys(configuredProps).length) { - configuredProps = { account_id: resolvedAccountId }; - } - - const response = await requestJson(config, { - method: 'POST', - path: `/platform/v1/integrations/actions/${actionId}/configure_prop`, - body: { - prop_name: propName, - configured_props: configuredProps, - dynamic_props_id: getFlag(parsed.flags, 'dynamic-props-id') - } - }); - - if (!response.ok) { - printJson(err('Failed to configure prop', response.raw, false, response.status)); - return 2; - } - - printJson(ok({ result: response.data })); - return 0; -} - -main().catch((error) => { - printJson(err('Unhandled error', { message: String(error?.message || error) })); - process.exit(1); -}); diff --git a/plugins/kapso/skills/automate-whatsapp/scripts/create-connect-token.js b/plugins/kapso/skills/automate-whatsapp/scripts/create-connect-token.js deleted file mode 100644 index 5386155..0000000 --- a/plugins/kapso/skills/automate-whatsapp/scripts/create-connect-token.js +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env node -import { loadConfig, requestJson } from './lib/workflows/kapso-api.js'; -import { ok, err, printJson } from './lib/workflows/result.js'; -import { parseArgs, getBooleanFlag } from './lib/workflows/args.js'; - -function usage() { - return ok({ - usage: 'node scripts/create-connect-token.js', - env: ['KAPSO_API_BASE_URL', 'KAPSO_API_KEY'] - }); -} - -async function main() { - const parsed = parseArgs(process.argv.slice(2)); - if (getBooleanFlag(parsed.flags, 'help') || getBooleanFlag(parsed.flags, 'h')) { - printJson(usage()); - return 0; - } - - const config = loadConfig(); - const response = await requestJson(config, { - method: 'POST', - path: '/platform/v1/integrations/connect_token' - }); - - if (!response.ok) { - printJson(err('Failed to create connect token', response.raw, false, response.status)); - return 2; - } - - printJson(ok({ connect_token: response.data })); - return 0; -} - -main().catch((error) => { - printJson(err('Unhandled error', { message: String(error?.message || error) })); - process.exit(1); -}); diff --git a/plugins/kapso/skills/automate-whatsapp/scripts/create-integration.js b/plugins/kapso/skills/automate-whatsapp/scripts/create-integration.js deleted file mode 100644 index c04d368..0000000 --- a/plugins/kapso/skills/automate-whatsapp/scripts/create-integration.js +++ /dev/null @@ -1,137 +0,0 @@ -#!/usr/bin/env node -import { loadConfig, requestJson } from './lib/workflows/kapso-api.js'; -import { ok, err, printJson } from './lib/workflows/result.js'; -import { parseArgs, getFlag, getBooleanFlag } from './lib/workflows/args.js'; - -function usage() { - return ok({ - usage: 'node scripts/create-integration.js --action-id --app-slug --account-id [--configured-props ] [--name ] [--app-name ] [--variable-definitions ] [--dynamic-props-id ]', - notes: [ - 'Use accounts[].pipedream_account_id for --account-id.', - 'If you pass an internal account UUID, the script will try to resolve it.', - 'Use --variable-definitions to define agent tool inputs (placeholders in configured_props become inputs).' - ], - env: ['KAPSO_API_BASE_URL', 'KAPSO_API_KEY'] - }); -} - -function parseJson(value, label) { - if (!value) return undefined; - try { - return JSON.parse(value); - } catch (error) { - throw new Error(`Invalid JSON for ${label}: ${String(error?.message || error)}`); - } -} - -function ensureAccountId(props, accountId) { - if (!props || !Object.keys(props).length) { - return { account_id: accountId }; - } - if (!props.account_id && !props.accountId) { - return { ...props, account_id: accountId }; - } - return props; -} - -function normalizeAccounts(payload) { - if (Array.isArray(payload?.accounts)) return payload.accounts; - if (Array.isArray(payload?.accounts?.accounts)) return payload.accounts.accounts; - if (Array.isArray(payload)) return payload; - return []; -} - -async function resolveAccountId(config, appSlug, accountId) { - if (accountId.startsWith('apn_')) return accountId; - - const response = await requestJson(config, { - method: 'GET', - path: '/platform/v1/integrations/accounts', - query: { app_slug: appSlug } - }); - - if (!response.ok) { - throw new Error('Unable to resolve account id. Use list-accounts and pass pipedream_account_id.'); - } - - const accounts = normalizeAccounts(response.data); - const match = accounts.find((account) => account.id === accountId); - if (match?.pipedream_account_id) return match.pipedream_account_id; - - throw new Error('account-id must be pipedream_account_id (use list-accounts output).'); -} - -async function main() { - const parsed = parseArgs(process.argv.slice(2)); - if (getBooleanFlag(parsed.flags, 'help') || getBooleanFlag(parsed.flags, 'h')) { - printJson(usage()); - return 0; - } - - const actionId = getFlag(parsed.flags, 'action-id'); - const appSlug = getFlag(parsed.flags, 'app-slug'); - const accountId = getFlag(parsed.flags, 'account-id'); - - if (!actionId || !appSlug || !accountId) { - printJson(err('action-id, app-slug, and account-id are required')); - return 2; - } - - let configuredProps; - let variableDefinitions; - - try { - configuredProps = parseJson(getFlag(parsed.flags, 'configured-props'), 'configured-props'); - variableDefinitions = parseJson(getFlag(parsed.flags, 'variable-definitions'), 'variable-definitions'); - } catch (error) { - printJson(err('Failed to parse JSON', { message: error.message })); - return 2; - } - - const config = loadConfig(); - let resolvedAccountId = accountId; - - try { - resolvedAccountId = await resolveAccountId(config, appSlug, accountId); - } catch (error) { - printJson(err('Failed to resolve account-id', { message: error.message })); - return 2; - } - - configuredProps = ensureAccountId(configuredProps, resolvedAccountId); - - const payload = { - action_id: actionId, - app_slug: appSlug, - account_id: resolvedAccountId, - configured_props: configuredProps - }; - - const name = getFlag(parsed.flags, 'name'); - const appName = getFlag(parsed.flags, 'app-name'); - const dynamicPropsId = getFlag(parsed.flags, 'dynamic-props-id'); - - if (name) payload.name = name; - if (appName) payload.app_name = appName; - if (dynamicPropsId) payload.dynamic_props_id = dynamicPropsId; - if (variableDefinitions) payload.variable_definitions = variableDefinitions; - - const response = await requestJson(config, { - method: 'POST', - path: '/platform/v1/integrations', - body: payload - }); - - if (!response.ok) { - printJson(err('Failed to create integration', response.raw, false, response.status)); - return 2; - } - - printJson(ok({ integration: response.data })); - return 0; -} - -main().catch((error) => { - printJson(err('Unhandled error', { message: String(error?.message || error) })); - process.exit(1); -}); diff --git a/plugins/kapso/skills/automate-whatsapp/scripts/create-row.js b/plugins/kapso/skills/automate-whatsapp/scripts/create-row.js deleted file mode 100644 index e80a93b..0000000 --- a/plugins/kapso/skills/automate-whatsapp/scripts/create-row.js +++ /dev/null @@ -1,47 +0,0 @@ -import { kapsoConfigFromEnv, kapsoRequest } from './lib/databases/kapso-api.js'; -import { hasHelpFlag, parseFlags, requireFlag, parseJsonObject } from './lib/databases/args.js'; - -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/create-row.js --table --data ', - env: ['KAPSO_API_BASE_URL', 'KAPSO_API_KEY'] - }, - null, - 2 - ) - ); - return 0; - } - - try { - const flags = parseFlags(argv); - const table = requireFlag(flags, 'table'); - const dataPayload = parseJsonObject(flags.data, 'data'); - const config = kapsoConfigFromEnv(); - const data = await kapsoRequest(config, `/platform/v1/db/${encodeURIComponent(table)}`, { - method: 'POST', - body: JSON.stringify(dataPayload) - }); - 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; - } -} - -main().then((code) => process.exit(code)); diff --git a/plugins/kapso/skills/automate-whatsapp/scripts/delete-integration.js b/plugins/kapso/skills/automate-whatsapp/scripts/delete-integration.js deleted file mode 100644 index 8ffaa83..0000000 --- a/plugins/kapso/skills/automate-whatsapp/scripts/delete-integration.js +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env node -import { loadConfig, requestJson } from './lib/workflows/kapso-api.js'; -import { ok, err, printJson } from './lib/workflows/result.js'; -import { parseArgs, getFlag, getBooleanFlag } from './lib/workflows/args.js'; - -function usage() { - return ok({ - usage: 'node scripts/delete-integration.js --integration-id ', - env: ['KAPSO_API_BASE_URL', 'KAPSO_API_KEY'] - }); -} - -async function main() { - const parsed = parseArgs(process.argv.slice(2)); - if (getBooleanFlag(parsed.flags, 'help') || getBooleanFlag(parsed.flags, 'h')) { - printJson(usage()); - return 0; - } - - const integrationId = getFlag(parsed.flags, 'integration-id'); - if (!integrationId) { - printJson(err('integration-id is required')); - return 2; - } - - const config = loadConfig(); - const response = await requestJson(config, { - method: 'DELETE', - path: `/platform/v1/integrations/${integrationId}` - }); - - if (!response.ok) { - printJson(err('Failed to delete integration', response.raw, false, response.status)); - return 2; - } - - printJson(ok({ deleted: true, status: response.status })); - return 0; -} - -main().catch((error) => { - printJson(err('Unhandled error', { message: String(error?.message || error) })); - process.exit(1); -}); diff --git a/plugins/kapso/skills/automate-whatsapp/scripts/delete-row.js b/plugins/kapso/skills/automate-whatsapp/scripts/delete-row.js deleted file mode 100644 index 89ca03c..0000000 --- a/plugins/kapso/skills/automate-whatsapp/scripts/delete-row.js +++ /dev/null @@ -1,49 +0,0 @@ -import { kapsoConfigFromEnv, kapsoRequest } from './lib/databases/kapso-api.js'; -import { hasHelpFlag, parseFlags, requireFlag } from './lib/databases/args.js'; -import { resolveFilters, filtersToQuery } from './lib/databases/filters.js'; - -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/delete-row.js --table (--id | --filters )', - env: ['KAPSO_API_BASE_URL', 'KAPSO_API_KEY'] - }, - null, - 2 - ) - ); - return 0; - } - - try { - const flags = parseFlags(argv); - const table = requireFlag(flags, 'table'); - const filters = resolveFilters(flags); - const query = filtersToQuery(filters); - const config = kapsoConfigFromEnv(); - const data = await kapsoRequest(config, `/platform/v1/db/${encodeURIComponent(table)}${query}`, { - method: 'DELETE' - }); - 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; - } -} - -main().then((code) => process.exit(code)); diff --git a/plugins/kapso/skills/automate-whatsapp/scripts/get-action-schema.js b/plugins/kapso/skills/automate-whatsapp/scripts/get-action-schema.js deleted file mode 100644 index 4f04ab2..0000000 --- a/plugins/kapso/skills/automate-whatsapp/scripts/get-action-schema.js +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env node -import { loadConfig, requestJson } from './lib/workflows/kapso-api.js'; -import { ok, err, printJson } from './lib/workflows/result.js'; -import { parseArgs, getFlag, getBooleanFlag } from './lib/workflows/args.js'; - -function usage() { - return ok({ - usage: 'node scripts/get-action-schema.js --action-id ', - env: ['KAPSO_API_BASE_URL', 'KAPSO_API_KEY'] - }); -} - -async function main() { - const parsed = parseArgs(process.argv.slice(2)); - if (getBooleanFlag(parsed.flags, 'help') || getBooleanFlag(parsed.flags, 'h')) { - printJson(usage()); - return 0; - } - - const actionId = getFlag(parsed.flags, 'action-id'); - if (!actionId) { - printJson(err('action-id is required')); - return 2; - } - - const config = loadConfig(); - const response = await requestJson(config, { - method: 'GET', - path: `/platform/v1/integrations/actions/${actionId}/schema` - }); - - if (!response.ok) { - printJson(err('Failed to fetch action schema', response.raw, false, response.status)); - return 2; - } - - printJson(ok({ schema: response.data })); - return 0; -} - -main().catch((error) => { - printJson(err('Unhandled error', { message: String(error?.message || error) })); - process.exit(1); -}); diff --git a/plugins/kapso/skills/automate-whatsapp/scripts/get-table.js b/plugins/kapso/skills/automate-whatsapp/scripts/get-table.js deleted file mode 100644 index 5738583..0000000 --- a/plugins/kapso/skills/automate-whatsapp/scripts/get-table.js +++ /dev/null @@ -1,45 +0,0 @@ -import { kapsoConfigFromEnv, kapsoRequest } from './lib/databases/kapso-api.js'; -import { hasHelpFlag, parseFlags, requireFlag, parseNumber } from './lib/databases/args.js'; - -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/get-table.js --table [--limit ]', - env: ['KAPSO_API_BASE_URL', 'KAPSO_API_KEY'] - }, - null, - 2 - ) - ); - return 0; - } - - try { - const flags = parseFlags(argv); - const table = requireFlag(flags, 'table'); - const limit = parseNumber(flags.limit, 'limit'); - const query = limit !== undefined ? `?limit=${encodeURIComponent(String(limit))}` : ''; - const config = kapsoConfigFromEnv(); - const data = await kapsoRequest(config, `/platform/v1/database_tables/${encodeURIComponent(table)}${query}`); - 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; - } -} - -main().then((code) => process.exit(code)); diff --git a/plugins/kapso/skills/automate-whatsapp/scripts/lib/databases/args.js b/plugins/kapso/skills/automate-whatsapp/scripts/lib/databases/args.js deleted file mode 100644 index 9c256e8..0000000 --- a/plugins/kapso/skills/automate-whatsapp/scripts/lib/databases/args.js +++ /dev/null @@ -1,87 +0,0 @@ -function hasHelpFlag(argv) { - return argv.includes('--help') || argv.includes('-h'); -} - -function parseFlags(argv) { - const flags = {}; - for (let index = 0; index < argv.length; index += 1) { - const arg = argv[index]; - if (!arg.startsWith('--')) { - continue; - } - const trimmed = arg.slice(2); - const eqIndex = trimmed.indexOf('='); - if (eqIndex >= 0) { - const key = trimmed.slice(0, eqIndex); - const value = trimmed.slice(eqIndex + 1); - flags[key] = value; - continue; - } - const next = argv[index + 1]; - if (!next || next.startsWith('--')) { - flags[trimmed] = true; - continue; - } - flags[trimmed] = next; - index += 1; - } - return flags; -} - -function requireFlag(flags, name) { - const value = flags[name]; - if (typeof value !== 'string' || value.length === 0) { - throw new Error(`Missing required flag --${name}`); - } - return value; -} - -function parseJsonObject(value, name) { - if (value === undefined || value === true) { - throw new Error(`Missing required JSON for --${name}`); - } - try { - const parsed = JSON.parse(value); - if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) { - throw new Error('Expected JSON object'); - } - return parsed; - } catch (error) { - throw new Error(`Invalid JSON for --${name}: ${String(error)}`); - } -} - -function parseJsonObjectOptional(value, name) { - if (value === undefined || value === true) { - return undefined; - } - try { - const parsed = JSON.parse(value); - if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) { - throw new Error('Expected JSON object'); - } - return parsed; - } catch (error) { - throw new Error(`Invalid JSON for --${name}: ${String(error)}`); - } -} - -function parseNumber(value, name) { - if (value === undefined || value === true) { - return undefined; - } - const parsed = Number(value); - if (Number.isNaN(parsed)) { - throw new Error(`Invalid number for --${name}: ${value}`); - } - return parsed; -} - -export { - hasHelpFlag, - parseFlags, - requireFlag, - parseJsonObject, - parseJsonObjectOptional, - parseNumber -}; diff --git a/plugins/kapso/skills/automate-whatsapp/scripts/lib/databases/filters.js b/plugins/kapso/skills/automate-whatsapp/scripts/lib/databases/filters.js deleted file mode 100644 index da4463d..0000000 --- a/plugins/kapso/skills/automate-whatsapp/scripts/lib/databases/filters.js +++ /dev/null @@ -1,30 +0,0 @@ -import { parseJsonObjectOptional } from './args.js'; - -function resolveFilters(flags) { - const filters = parseJsonObjectOptional(flags.filters, 'filters'); - if (filters) { - const entries = {}; - Object.entries(filters).forEach(([key, value]) => { - entries[key] = String(value); - }); - return entries; - } - if (typeof flags.id === 'string' && flags.id.length > 0) { - return { id: `eq.${flags.id}` }; - } - throw new Error('Provide --filters (JSON) or --id'); -} - -function filtersToQuery(filters) { - const params = new URLSearchParams(); - Object.entries(filters).forEach(([key, value]) => { - params.set(key, value); - }); - const query = params.toString(); - return query ? `?${query}` : ''; -} - -export { - resolveFilters, - filtersToQuery -}; diff --git a/plugins/kapso/skills/automate-whatsapp/scripts/lib/databases/kapso-api.js b/plugins/kapso/skills/automate-whatsapp/scripts/lib/databases/kapso-api.js deleted file mode 100644 index 8d8a6a7..0000000 --- a/plugins/kapso/skills/automate-whatsapp/scripts/lib/databases/kapso-api.js +++ /dev/null @@ -1,86 +0,0 @@ -function requireEnv(name) { - const value = process.env[name]; - if (!value) { - throw new Error(`Missing required env var: ${name}`); - } - return value; -} - -function normalizeBaseUrl(raw) { - return raw.replace(/\/+$/, ''); -} - -function isLocalhost(hostname) { - return hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '0.0.0.0' || hostname === '::1' || hostname === '[::1]'; -} - -function validateBaseUrl(baseUrl) { - if (!baseUrl) return; - let parsed; - try { - parsed = new URL(baseUrl); - } catch (error) { - throw new Error('Invalid KAPSO_API_BASE_URL.'); - } - if (parsed.username || parsed.password) { - throw new Error('KAPSO_API_BASE_URL must not include credentials.'); - } - if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') { - throw new Error('KAPSO_API_BASE_URL must use http or https.'); - } - if (!process.env.KAPSO_API_ALLOW_LOCALHOST && isLocalhost(parsed.hostname)) { - throw new Error( - `KAPSO_API_BASE_URL points to localhost (${parsed.hostname}). ` + - 'Set KAPSO_API_ALLOW_LOCALHOST=true if this is intentional.' - ); - } - if ( - parsed.protocol === 'http:' && - !(process.env.KAPSO_API_ALLOW_LOCALHOST && isLocalhost(parsed.hostname)) && - process.env.KAPSO_API_ALLOW_INSECURE_HTTP !== 'true' - ) { - throw new Error( - 'KAPSO_API_BASE_URL must use https. ' + - 'Set KAPSO_API_ALLOW_INSECURE_HTTP=true only for trusted development hosts.' - ); - } -} - -function kapsoConfigFromEnv() { - const baseUrl = normalizeBaseUrl(requireEnv('KAPSO_API_BASE_URL')); - validateBaseUrl(baseUrl); - return { - baseUrl, - apiKey: requireEnv('KAPSO_API_KEY') - }; -} - -async function kapsoRequest(config, path, init = {}) { - const url = `${config.baseUrl}${path}`; - const headers = new Headers(init.headers || undefined); - headers.set('X-API-Key', config.apiKey); - if (!headers.has('Content-Type')) { - headers.set('Content-Type', 'application/json'); - } - - let response; - try { - response = await fetch(url, { ...init, headers }); - } catch (error) { - throw new Error( - `Kapso API request failed (network error) url=${url} error=${String(error?.message || error)}` - ); - } - const text = await response.text(); - - if (!response.ok) { - throw new Error(`Kapso API request failed (status=${response.status}) body=${text}`); - } - - return text ? JSON.parse(text) : {}; -} - -export { - kapsoConfigFromEnv, - kapsoRequest -}; diff --git a/plugins/kapso/skills/automate-whatsapp/scripts/lib/functions/kapso-api.js b/plugins/kapso/skills/automate-whatsapp/scripts/lib/functions/kapso-api.js index 6afa98f..d70f6ed 100644 --- a/plugins/kapso/skills/automate-whatsapp/scripts/lib/functions/kapso-api.js +++ b/plugins/kapso/skills/automate-whatsapp/scripts/lib/functions/kapso-api.js @@ -11,7 +11,7 @@ function normalizeBaseUrl(raw) { } function isLocalhost(hostname) { - return hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '0.0.0.0' || hostname === '::1' || hostname === '[::1]'; + return hostname === 'localhost' || hostname === '127.0.0.1'; } function validateBaseUrl(baseUrl) { @@ -20,13 +20,7 @@ function validateBaseUrl(baseUrl) { try { parsed = new URL(baseUrl); } catch (error) { - throw new Error('Invalid KAPSO_API_BASE_URL.'); - } - if (parsed.username || parsed.password) { - throw new Error('KAPSO_API_BASE_URL must not include credentials.'); - } - if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') { - throw new Error('KAPSO_API_BASE_URL must use http or https.'); + throw new Error(`Invalid KAPSO_API_BASE_URL: ${baseUrl}`); } if (!process.env.KAPSO_API_ALLOW_LOCALHOST && isLocalhost(parsed.hostname)) { throw new Error( @@ -34,16 +28,6 @@ function validateBaseUrl(baseUrl) { 'Set KAPSO_API_ALLOW_LOCALHOST=true if this is intentional.' ); } - if ( - parsed.protocol === 'http:' && - !(process.env.KAPSO_API_ALLOW_LOCALHOST && isLocalhost(parsed.hostname)) && - process.env.KAPSO_API_ALLOW_INSECURE_HTTP !== 'true' - ) { - throw new Error( - 'KAPSO_API_BASE_URL must use https. ' + - 'Set KAPSO_API_ALLOW_INSECURE_HTTP=true only for trusted development hosts.' - ); - } } function kapsoConfigFromEnv() { diff --git a/plugins/kapso/skills/automate-whatsapp/scripts/lib/workflows/kapso-api.js b/plugins/kapso/skills/automate-whatsapp/scripts/lib/workflows/kapso-api.js index a9a769f..ccda3fc 100644 --- a/plugins/kapso/skills/automate-whatsapp/scripts/lib/workflows/kapso-api.js +++ b/plugins/kapso/skills/automate-whatsapp/scripts/lib/workflows/kapso-api.js @@ -11,7 +11,7 @@ function normalizeBaseUrl(raw) { } function isLocalhost(hostname) { - return hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '0.0.0.0' || hostname === '::1' || hostname === '[::1]'; + return hostname === 'localhost' || hostname === '127.0.0.1'; } function validateBaseUrl(baseUrl) { @@ -20,13 +20,7 @@ function validateBaseUrl(baseUrl) { try { parsed = new URL(baseUrl); } catch (error) { - throw new Error('Invalid KAPSO_API_BASE_URL.'); - } - if (parsed.username || parsed.password) { - throw new Error('KAPSO_API_BASE_URL must not include credentials.'); - } - if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') { - throw new Error('KAPSO_API_BASE_URL must use http or https.'); + throw new Error(`Invalid KAPSO_API_BASE_URL: ${baseUrl}`); } if (!process.env.KAPSO_API_ALLOW_LOCALHOST && isLocalhost(parsed.hostname)) { throw new Error( @@ -34,16 +28,6 @@ function validateBaseUrl(baseUrl) { 'Set KAPSO_API_ALLOW_LOCALHOST=true if this is intentional.' ); } - if ( - parsed.protocol === 'http:' && - !(process.env.KAPSO_API_ALLOW_LOCALHOST && isLocalhost(parsed.hostname)) && - process.env.KAPSO_API_ALLOW_INSECURE_HTTP !== 'true' - ) { - throw new Error( - 'KAPSO_API_BASE_URL must use https. ' + - 'Set KAPSO_API_ALLOW_INSECURE_HTTP=true only for trusted development hosts.' - ); - } } export function loadConfig(options = {}) { diff --git a/plugins/kapso/skills/automate-whatsapp/scripts/list-accounts.js b/plugins/kapso/skills/automate-whatsapp/scripts/list-accounts.js deleted file mode 100644 index b3458dc..0000000 --- a/plugins/kapso/skills/automate-whatsapp/scripts/list-accounts.js +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env node -import { loadConfig, requestJson } from './lib/workflows/kapso-api.js'; -import { ok, err, printJson } from './lib/workflows/result.js'; -import { parseArgs, getFlag, getBooleanFlag } from './lib/workflows/args.js'; - -function usage() { - return ok({ - usage: 'node scripts/list-accounts.js [--app-slug ]', - notes: [ - 'Use accounts[].pipedream_account_id for any --account-id flag.', - 'The internal accounts[].id will not work for integrations.' - ], - env: ['KAPSO_API_BASE_URL', 'KAPSO_API_KEY'] - }); -} - -function normalizeAccounts(payload) { - if (Array.isArray(payload?.accounts)) return payload.accounts; - if (Array.isArray(payload?.accounts?.accounts)) return payload.accounts.accounts; - if (Array.isArray(payload)) return payload; - return []; -} - -async function main() { - const parsed = parseArgs(process.argv.slice(2)); - if (getBooleanFlag(parsed.flags, 'help') || getBooleanFlag(parsed.flags, 'h')) { - printJson(usage()); - return 0; - } - - const config = loadConfig(); - const response = await requestJson(config, { - method: 'GET', - path: '/platform/v1/integrations/accounts', - query: { - app_slug: getFlag(parsed.flags, 'app-slug') - } - }); - - if (!response.ok) { - printJson(err('Failed to list accounts', response.raw, false, response.status)); - return 2; - } - - const raw = response.data; - const accounts = normalizeAccounts(raw).map((account) => ({ - ...account, - preferred_account_id: account.pipedream_account_id - })); - const payload = Array.isArray(raw) ? accounts : { ...raw, accounts }; - - printJson(ok({ - accounts: payload, - note: 'Use preferred_account_id (pipedream_account_id) for create-integration/configure-prop.' - })); - return 0; -} - -main().catch((error) => { - printJson(err('Unhandled error', { message: String(error?.message || error) })); - process.exit(1); -}); diff --git a/plugins/kapso/skills/automate-whatsapp/scripts/list-apps.js b/plugins/kapso/skills/automate-whatsapp/scripts/list-apps.js deleted file mode 100644 index cead5f4..0000000 --- a/plugins/kapso/skills/automate-whatsapp/scripts/list-apps.js +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env node -import { loadConfig, requestJson } from './lib/workflows/kapso-api.js'; -import { ok, err, printJson } from './lib/workflows/result.js'; -import { parseArgs, getFlag, getBooleanFlag, getNumberFlag } from './lib/workflows/args.js'; - -function usage() { - return ok({ - usage: 'node scripts/list-apps.js [--query ] [--limit ]', - env: ['KAPSO_API_BASE_URL', 'KAPSO_API_KEY'] - }); -} - -async function main() { - const parsed = parseArgs(process.argv.slice(2)); - if (getBooleanFlag(parsed.flags, 'help') || getBooleanFlag(parsed.flags, 'h')) { - printJson(usage()); - return 0; - } - - const config = loadConfig(); - const response = await requestJson(config, { - method: 'GET', - path: '/platform/v1/integrations/apps', - query: { - query: getFlag(parsed.flags, 'query'), - limit: getNumberFlag(parsed.flags, 'limit') - } - }); - - if (!response.ok) { - printJson(err('Failed to list apps', response.raw, false, response.status)); - return 2; - } - - printJson(ok({ apps: response.data })); - return 0; -} - -main().catch((error) => { - printJson(err('Unhandled error', { message: String(error?.message || error) })); - process.exit(1); -}); diff --git a/plugins/kapso/skills/automate-whatsapp/scripts/list-integrations.js b/plugins/kapso/skills/automate-whatsapp/scripts/list-integrations.js deleted file mode 100644 index aa6c2da..0000000 --- a/plugins/kapso/skills/automate-whatsapp/scripts/list-integrations.js +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env node -import { loadConfig, requestJson } from './lib/workflows/kapso-api.js'; -import { ok, err, printJson } from './lib/workflows/result.js'; -import { parseArgs, getBooleanFlag } from './lib/workflows/args.js'; - -function usage() { - return ok({ - usage: 'node scripts/list-integrations.js', - notes: [ - 'Check integrations[].variable_definitions to see required tool inputs.' - ], - env: ['KAPSO_API_BASE_URL', 'KAPSO_API_KEY'] - }); -} - -async function main() { - const parsed = parseArgs(process.argv.slice(2)); - if (getBooleanFlag(parsed.flags, 'help') || getBooleanFlag(parsed.flags, 'h')) { - printJson(usage()); - return 0; - } - - const config = loadConfig(); - const response = await requestJson(config, { - method: 'GET', - path: '/platform/v1/integrations' - }); - - if (!response.ok) { - printJson(err('Failed to list integrations', response.raw, false, response.status)); - return 2; - } - - printJson(ok({ integrations: response.data })); - return 0; -} - -main().catch((error) => { - printJson(err('Unhandled error', { message: String(error?.message || error) })); - process.exit(1); -}); diff --git a/plugins/kapso/skills/automate-whatsapp/scripts/list-tables.js b/plugins/kapso/skills/automate-whatsapp/scripts/list-tables.js deleted file mode 100644 index c389b7f..0000000 --- a/plugins/kapso/skills/automate-whatsapp/scripts/list-tables.js +++ /dev/null @@ -1,41 +0,0 @@ -import { kapsoConfigFromEnv, kapsoRequest } from './lib/databases/kapso-api.js'; -import { hasHelpFlag } from './lib/databases/args.js'; - -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/list-tables.js', - env: ['KAPSO_API_BASE_URL', 'KAPSO_API_KEY'] - }, - null, - 2 - ) - ); - return 0; - } - - try { - const config = kapsoConfigFromEnv(); - const data = await kapsoRequest(config, '/platform/v1/database_tables'); - 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; - } -} - -main().then((code) => process.exit(code)); diff --git a/plugins/kapso/skills/automate-whatsapp/scripts/query-rows.js b/plugins/kapso/skills/automate-whatsapp/scripts/query-rows.js deleted file mode 100644 index 6cac07e..0000000 --- a/plugins/kapso/skills/automate-whatsapp/scripts/query-rows.js +++ /dev/null @@ -1,71 +0,0 @@ -import { kapsoConfigFromEnv, kapsoRequest } from './lib/databases/kapso-api.js'; -import { hasHelpFlag, parseFlags, requireFlag, parseJsonObjectOptional, parseNumber } from './lib/databases/args.js'; - -function ok(data) { - return { ok: true, data }; -} - -function err(message, details) { - return { ok: false, error: { message, details } }; -} - -function buildQuery(flags) { - const params = new URLSearchParams(); - const filters = parseJsonObjectOptional(flags.filters, 'filters'); - if (filters) { - Object.entries(filters).forEach(([key, value]) => { - params.set(key, String(value)); - }); - } - if (typeof flags.select === 'string' && flags.select.length > 0) { - params.set('select', flags.select); - } - if (typeof flags.order === 'string' && flags.order.length > 0) { - params.set('order', flags.order); - } - const limit = parseNumber(flags.limit, 'limit'); - if (limit !== undefined) { - params.set('limit', String(limit)); - } - const offset = parseNumber(flags.offset, 'offset'); - if (offset !== undefined) { - params.set('offset', String(offset)); - } - const query = params.toString(); - return query ? `?${query}` : ''; -} - -async function main() { - const argv = process.argv.slice(2); - if (hasHelpFlag(argv)) { - console.log( - JSON.stringify( - { - ok: true, - usage: - 'node scripts/query-rows.js --table [--filters ] [--select ] [--order ] [--limit ] [--offset ]', - env: ['KAPSO_API_BASE_URL', 'KAPSO_API_KEY'] - }, - null, - 2 - ) - ); - return 0; - } - - try { - const flags = parseFlags(argv); - const table = requireFlag(flags, 'table'); - const query = buildQuery(flags); - const config = kapsoConfigFromEnv(); - const data = await kapsoRequest(config, `/platform/v1/db/${encodeURIComponent(table)}${query}`); - 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; - } -} - -main().then((code) => process.exit(code)); diff --git a/plugins/kapso/skills/automate-whatsapp/scripts/reload-props.js b/plugins/kapso/skills/automate-whatsapp/scripts/reload-props.js deleted file mode 100644 index 2bd9232..0000000 --- a/plugins/kapso/skills/automate-whatsapp/scripts/reload-props.js +++ /dev/null @@ -1,110 +0,0 @@ -#!/usr/bin/env node -import { loadConfig, requestJson } from './lib/workflows/kapso-api.js'; -import { ok, err, printJson } from './lib/workflows/result.js'; -import { parseArgs, getFlag, getBooleanFlag } from './lib/workflows/args.js'; - -function usage() { - return ok({ - usage: 'node scripts/reload-props.js --action-id --account-id [--configured-props ] [--dynamic-props-id ]', - notes: [ - 'Use accounts[].pipedream_account_id for --account-id.', - 'If you pass an internal account UUID, the script will try to resolve it.' - ], - env: ['KAPSO_API_BASE_URL', 'KAPSO_API_KEY'] - }); -} - -function parseJson(value, label) { - if (!value) return undefined; - try { - return JSON.parse(value); - } catch (error) { - throw new Error(`Invalid JSON for ${label}: ${String(error?.message || error)}`); - } -} - -function normalizeAccounts(payload) { - if (Array.isArray(payload?.accounts)) return payload.accounts; - if (Array.isArray(payload?.accounts?.accounts)) return payload.accounts.accounts; - if (Array.isArray(payload)) return payload; - return []; -} - -async function resolveAccountId(config, accountId) { - if (accountId.startsWith('apn_')) return accountId; - - const response = await requestJson(config, { - method: 'GET', - path: '/platform/v1/integrations/accounts' - }); - - if (!response.ok) { - throw new Error('Unable to resolve account id. Use list-accounts and pass pipedream_account_id.'); - } - - const accounts = normalizeAccounts(response.data); - const match = accounts.find((account) => account.id === accountId); - if (match?.pipedream_account_id) return match.pipedream_account_id; - - throw new Error('account-id must be pipedream_account_id (use list-accounts output).'); -} - -async function main() { - const parsed = parseArgs(process.argv.slice(2)); - if (getBooleanFlag(parsed.flags, 'help') || getBooleanFlag(parsed.flags, 'h')) { - printJson(usage()); - return 0; - } - - const actionId = getFlag(parsed.flags, 'action-id'); - const accountId = getFlag(parsed.flags, 'account-id'); - if (!actionId || !accountId) { - printJson(err('action-id and account-id are required')); - return 2; - } - - let configuredProps = {}; - try { - const provided = parseJson(getFlag(parsed.flags, 'configured-props'), 'configured-props'); - configuredProps = provided || {}; - } catch (error) { - printJson(err('Failed to parse configured-props', { message: error.message })); - return 2; - } - - const config = loadConfig(); - let resolvedAccountId = accountId; - - try { - resolvedAccountId = await resolveAccountId(config, accountId); - } catch (error) { - printJson(err('Failed to resolve account-id', { message: error.message })); - return 2; - } - - if (!Object.keys(configuredProps).length) { - configuredProps = { account_id: resolvedAccountId }; - } - - const response = await requestJson(config, { - method: 'POST', - path: `/platform/v1/integrations/actions/${actionId}/reload_props`, - body: { - configured_props: configuredProps, - dynamic_props_id: getFlag(parsed.flags, 'dynamic-props-id') - } - }); - - if (!response.ok) { - printJson(err('Failed to reload props', response.raw, false, response.status)); - return 2; - } - - printJson(ok({ result: response.data })); - return 0; -} - -main().catch((error) => { - printJson(err('Unhandled error', { message: String(error?.message || error) })); - process.exit(1); -}); diff --git a/plugins/kapso/skills/automate-whatsapp/scripts/search-actions.js b/plugins/kapso/skills/automate-whatsapp/scripts/search-actions.js deleted file mode 100644 index aba69a1..0000000 --- a/plugins/kapso/skills/automate-whatsapp/scripts/search-actions.js +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env node -import { loadConfig, requestJson } from './lib/workflows/kapso-api.js'; -import { ok, err, printJson } from './lib/workflows/result.js'; -import { parseArgs, getFlag, getBooleanFlag } from './lib/workflows/args.js'; - -function usage() { - return ok({ - usage: 'node scripts/search-actions.js --query [--app-slug ]', - notes: [ - 'Prefer one-word queries (ex: "calendar", "slack", "hubspot").', - 'Use --app-slug to narrow results to a single app.', - 'action_id equals the action key returned in results.' - ], - env: ['KAPSO_API_BASE_URL', 'KAPSO_API_KEY'] - }); -} - -async function main() { - const parsed = parseArgs(process.argv.slice(2)); - if (getBooleanFlag(parsed.flags, 'help') || getBooleanFlag(parsed.flags, 'h')) { - printJson(usage()); - return 0; - } - - const query = getFlag(parsed.flags, 'query'); - if (!query) { - printJson(err('query is required')); - return 2; - } - - const config = loadConfig(); - const response = await requestJson(config, { - method: 'GET', - path: '/platform/v1/integrations/actions', - query: { - query, - app_slug: getFlag(parsed.flags, 'app-slug') - } - }); - - if (!response.ok) { - printJson(err('Failed to search actions', response.raw, false, response.status)); - return 2; - } - - const raw = response.data; - const actions = Array.isArray(raw?.actions) ? raw.actions : (Array.isArray(raw) ? raw : []); - const mapped = actions.map((action) => ({ - ...action, - action_id: action.action_id || action.key - })); - const payload = Array.isArray(raw) ? mapped : { ...raw, actions: mapped }; - - printJson(ok({ - actions: payload, - note: 'Use action_id (same as key) with get-action-schema/create-integration.' - })); - return 0; -} - -main().catch((error) => { - printJson(err('Unhandled error', { message: String(error?.message || error) })); - process.exit(1); -}); diff --git a/plugins/kapso/skills/automate-whatsapp/scripts/update-integration.js b/plugins/kapso/skills/automate-whatsapp/scripts/update-integration.js deleted file mode 100644 index 44ba34e..0000000 --- a/plugins/kapso/skills/automate-whatsapp/scripts/update-integration.js +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/env node -import { loadConfig, requestJson } from './lib/workflows/kapso-api.js'; -import { ok, err, printJson } from './lib/workflows/result.js'; -import { parseArgs, getFlag, getBooleanFlag } from './lib/workflows/args.js'; - -function usage() { - return ok({ - usage: 'node scripts/update-integration.js --integration-id [--configured-props ] [--name ] [--variable-definitions ] [--dynamic-props-id ]', - notes: [ - 'Use --variable-definitions to update required tool input fields.' - ], - env: ['KAPSO_API_BASE_URL', 'KAPSO_API_KEY'] - }); -} - -function parseJson(value, label) { - if (!value) return undefined; - try { - return JSON.parse(value); - } catch (error) { - throw new Error(`Invalid JSON for ${label}: ${String(error?.message || error)}`); - } -} - -async function main() { - const parsed = parseArgs(process.argv.slice(2)); - if (getBooleanFlag(parsed.flags, 'help') || getBooleanFlag(parsed.flags, 'h')) { - printJson(usage()); - return 0; - } - - const integrationId = getFlag(parsed.flags, 'integration-id'); - if (!integrationId) { - printJson(err('integration-id is required')); - return 2; - } - - let configuredProps; - let variableDefinitions; - - try { - configuredProps = parseJson(getFlag(parsed.flags, 'configured-props'), 'configured-props'); - variableDefinitions = parseJson(getFlag(parsed.flags, 'variable-definitions'), 'variable-definitions'); - } catch (error) { - printJson(err('Failed to parse JSON', { message: error.message })); - return 2; - } - - const payload = {}; - const name = getFlag(parsed.flags, 'name'); - const dynamicPropsId = getFlag(parsed.flags, 'dynamic-props-id'); - - if (name) payload.name = name; - if (dynamicPropsId) payload.dynamic_props_id = dynamicPropsId; - if (configuredProps) payload.configured_props = configuredProps; - if (variableDefinitions) payload.variable_definitions = variableDefinitions; - - if (!Object.keys(payload).length) { - printJson(err('No updates provided')); - return 2; - } - - const config = loadConfig(); - const response = await requestJson(config, { - method: 'PATCH', - path: `/platform/v1/integrations/${integrationId}`, - body: payload - }); - - if (!response.ok) { - printJson(err('Failed to update integration', response.raw, false, response.status)); - return 2; - } - - printJson(ok({ integration: response.data })); - return 0; -} - -main().catch((error) => { - printJson(err('Unhandled error', { message: String(error?.message || error) })); - process.exit(1); -}); diff --git a/plugins/kapso/skills/automate-whatsapp/scripts/update-row.js b/plugins/kapso/skills/automate-whatsapp/scripts/update-row.js deleted file mode 100644 index 490d686..0000000 --- a/plugins/kapso/skills/automate-whatsapp/scripts/update-row.js +++ /dev/null @@ -1,51 +0,0 @@ -import { kapsoConfigFromEnv, kapsoRequest } from './lib/databases/kapso-api.js'; -import { hasHelpFlag, parseFlags, requireFlag, parseJsonObject } from './lib/databases/args.js'; -import { resolveFilters, filtersToQuery } from './lib/databases/filters.js'; - -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/update-row.js --table --data (--id | --filters )', - env: ['KAPSO_API_BASE_URL', 'KAPSO_API_KEY'] - }, - null, - 2 - ) - ); - return 0; - } - - try { - const flags = parseFlags(argv); - const table = requireFlag(flags, 'table'); - const dataPayload = parseJsonObject(flags.data, 'data'); - const filters = resolveFilters(flags); - const query = filtersToQuery(filters); - const config = kapsoConfigFromEnv(); - const data = await kapsoRequest(config, `/platform/v1/db/${encodeURIComponent(table)}${query}`, { - method: 'PATCH', - body: JSON.stringify(dataPayload) - }); - 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; - } -} - -main().then((code) => process.exit(code)); diff --git a/plugins/kapso/skills/automate-whatsapp/scripts/upsert-row.js b/plugins/kapso/skills/automate-whatsapp/scripts/upsert-row.js deleted file mode 100644 index 3cb3a4d..0000000 --- a/plugins/kapso/skills/automate-whatsapp/scripts/upsert-row.js +++ /dev/null @@ -1,64 +0,0 @@ -import { kapsoConfigFromEnv, kapsoRequest } from './lib/databases/kapso-api.js'; -import { hasHelpFlag, parseFlags, requireFlag, parseJsonObject } from './lib/databases/args.js'; -import { resolveFilters, filtersToQuery } from './lib/databases/filters.js'; - -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/upsert-row.js --table --data [--upsert-key ] [--filters | --id ]', - env: ['KAPSO_API_BASE_URL', 'KAPSO_API_KEY'] - }, - null, - 2 - ) - ); - return 0; - } - - try { - const flags = parseFlags(argv); - const table = requireFlag(flags, 'table'); - const dataPayload = parseJsonObject(flags.data, 'data'); - - let filters; - if (typeof flags['upsert-key'] === 'string' && flags['upsert-key'].length > 0) { - const key = flags['upsert-key']; - const value = dataPayload[key]; - if (value === undefined) { - throw new Error(`--data must include ${key} when using --upsert-key`); - } - filters = { [key]: `eq.${String(value)}` }; - } else { - filters = resolveFilters(flags); - } - - const query = filtersToQuery(filters); - const config = kapsoConfigFromEnv(); - const data = await kapsoRequest(config, `/platform/v1/db/${encodeURIComponent(table)}${query}`, { - method: 'PUT', - body: JSON.stringify(dataPayload) - }); - - 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; - } -} - -main().then((code) => process.exit(code)); diff --git a/plugins/kapso/skills/automate-whatsapp/scripts/validate-graph.js b/plugins/kapso/skills/automate-whatsapp/scripts/validate-graph.js index 15743db..319cd06 100644 --- a/plugins/kapso/skills/automate-whatsapp/scripts/validate-graph.js +++ b/plugins/kapso/skills/automate-whatsapp/scripts/validate-graph.js @@ -14,7 +14,6 @@ const SUPPORTED_NODE_TYPES = new Set([ 'decide', 'call', 'webhook', - 'pipedream', 'function', 'agent', 'handoff' diff --git a/plugins/kapso/skills/automate-whatsapp/scripts/variables-set.js b/plugins/kapso/skills/automate-whatsapp/scripts/variables-set.js index 2dfcd4a..9bcc03f 100644 --- a/plugins/kapso/skills/automate-whatsapp/scripts/variables-set.js +++ b/plugins/kapso/skills/automate-whatsapp/scripts/variables-set.js @@ -1,4 +1,5 @@ #!/usr/bin/env node +import { loadConfig } from './lib/workflows/kapso-api.js'; import { ok, err, printJson } from './lib/workflows/result.js'; import { parseArgs, getFlag, getBooleanFlag } from './lib/workflows/args.js'; @@ -20,10 +21,12 @@ async function main() { const name = getFlag(parsed.flags, 'name'); const value = getFlag(parsed.flags, 'value'); + const config = loadConfig(); + printJson(err('Workflow variables create/update is not available in the Platform API.', { workflow_id: workflowId, name, - value_provided: value !== undefined, + value, note: 'Only variable discovery is proposed; CRUD endpoints are missing.' }, true)); diff --git a/plugins/kapso/skills/integrate-whatsapp/references/detecting-whatsapp-connection.md b/plugins/kapso/skills/integrate-whatsapp/references/detecting-whatsapp-connection.md index 3900ca2..e4a6987 100644 --- a/plugins/kapso/skills/integrate-whatsapp/references/detecting-whatsapp-connection.md +++ b/plugins/kapso/skills/integrate-whatsapp/references/detecting-whatsapp-connection.md @@ -11,8 +11,8 @@ Configure a project webhook to receive the `whatsapp.phone_number.created` event ### Setup -1. Open the sidebar and click **Webhooks** -2. Go to the **Project webhooks** tab +1. Open the sidebar and click **Integrations → Webhooks** +2. Go to the **Platform webhooks** tab 3. Click **Add Webhook** 4. Enter your HTTPS endpoint URL 5. Copy the auto-generated secret key diff --git a/plugins/kapso/skills/integrate-whatsapp/references/platform-api-reference.md b/plugins/kapso/skills/integrate-whatsapp/references/platform-api-reference.md index f3ca2ba..000aff3 100644 --- a/plugins/kapso/skills/integrate-whatsapp/references/platform-api-reference.md +++ b/plugins/kapso/skills/integrate-whatsapp/references/platform-api-reference.md @@ -111,19 +111,6 @@ Functions: - `POST /functions/:id/invoke` - `GET /functions/:id/invocations` -Integrations: -- `GET /integrations` -- `POST /integrations` -- `PATCH /integrations/:id` -- `DELETE /integrations/:id` -- `GET /integrations/apps` -- `GET /integrations/actions` -- `GET /integrations/accounts` -- `POST /integrations/connect_token` -- `GET /integrations/actions/:action_id/schema` -- `POST /integrations/actions/:action_id/configure_prop` -- `POST /integrations/actions/:action_id/reload_props` - Logs: - `GET /api_logs` - `GET /webhook_deliveries` diff --git a/plugins/kapso/skills/integrate-whatsapp/scripts/lib/env.js b/plugins/kapso/skills/integrate-whatsapp/scripts/lib/env.js index 67b96ac..af9d93b 100644 --- a/plugins/kapso/skills/integrate-whatsapp/scripts/lib/env.js +++ b/plugins/kapso/skills/integrate-whatsapp/scripts/lib/env.js @@ -14,41 +14,6 @@ function normalizeBaseUrl(raw) { return trimmed; } -function isLocalhost(hostname) { - return hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '0.0.0.0' || hostname === '::1' || hostname === '[::1]'; -} - -function validateBaseUrl(baseUrl) { - let parsed; - try { - parsed = new URL(baseUrl); - } catch { - throw new Error('Invalid KAPSO_API_BASE_URL.'); - } - if (parsed.username || parsed.password) { - throw new Error('KAPSO_API_BASE_URL must not include credentials.'); - } - if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') { - throw new Error('KAPSO_API_BASE_URL must use http or https.'); - } - if (!process.env.KAPSO_API_ALLOW_LOCALHOST && isLocalhost(parsed.hostname)) { - throw new Error( - `KAPSO_API_BASE_URL points to localhost (${parsed.hostname}). ` + - 'Set KAPSO_API_ALLOW_LOCALHOST=true if this is intentional.' - ); - } - if ( - parsed.protocol === 'http:' && - !(process.env.KAPSO_API_ALLOW_LOCALHOST && isLocalhost(parsed.hostname)) && - process.env.KAPSO_API_ALLOW_INSECURE_HTTP !== 'true' - ) { - throw new Error( - 'KAPSO_API_BASE_URL must use https. ' + - 'Set KAPSO_API_ALLOW_INSECURE_HTTP=true only for trusted development hosts.' - ); - } -} - function normalizeGraphVersion(version) { if (!version) return DEFAULT_GRAPH_VERSION; return version.startsWith('v') ? version : `v${version}`; @@ -56,7 +21,6 @@ function normalizeGraphVersion(version) { function getConfig() { const baseUrl = normalizeBaseUrl(requireEnv('KAPSO_API_BASE_URL')); - validateBaseUrl(baseUrl); return { baseUrl, apiKey: requireEnv('KAPSO_API_KEY'), diff --git a/plugins/kapso/skills/integrate-whatsapp/scripts/lib/env.mjs b/plugins/kapso/skills/integrate-whatsapp/scripts/lib/env.mjs index 42b8b2b..f60296d 100644 --- a/plugins/kapso/skills/integrate-whatsapp/scripts/lib/env.mjs +++ b/plugins/kapso/skills/integrate-whatsapp/scripts/lib/env.mjs @@ -27,46 +27,9 @@ function normalizeGraphVersion(value) { return value.startsWith('v') ? value : `v${value}`; } -function isLocalhost(hostname) { - return hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '0.0.0.0' || hostname === '::1' || hostname === '[::1]'; -} - -function validateBaseUrl(baseUrl, label) { - let parsed; - try { - parsed = new URL(baseUrl); - } catch { - throw new Error(`Invalid ${label}.`); - } - if (parsed.username || parsed.password) { - throw new Error(`${label} must not include credentials.`); - } - if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') { - throw new Error(`${label} must use http or https.`); - } - if (!process.env.KAPSO_API_ALLOW_LOCALHOST && isLocalhost(parsed.hostname)) { - throw new Error( - `${label} points to localhost (${parsed.hostname}). ` + - 'Set KAPSO_API_ALLOW_LOCALHOST=true if this is intentional.' - ); - } - if ( - parsed.protocol === 'http:' && - !(process.env.KAPSO_API_ALLOW_LOCALHOST && isLocalhost(parsed.hostname)) && - process.env.KAPSO_API_ALLOW_INSECURE_HTTP !== 'true' - ) { - throw new Error( - `${label} must use https. ` + - 'Set KAPSO_API_ALLOW_INSECURE_HTTP=true only for trusted development hosts.' - ); - } -} - export function metaProxyConfig() { const rawBase = process.env.KAPSO_META_BASE_URL || requireEnv('KAPSO_API_BASE_URL'); const baseUrl = normalizeMetaBase(rawBase); - const baseLabel = process.env.KAPSO_META_BASE_URL ? 'KAPSO_META_BASE_URL' : 'KAPSO_API_BASE_URL'; - validateBaseUrl(baseUrl, baseLabel); const apiKey = requireEnv('KAPSO_API_KEY'); const graphVersion = normalizeGraphVersion(process.env.META_GRAPH_VERSION || 'v24.0'); diff --git a/plugins/kapso/skills/integrate-whatsapp/scripts/lib/webhooks/kapso-api.js b/plugins/kapso/skills/integrate-whatsapp/scripts/lib/webhooks/kapso-api.js index f71e5b3..caf14de 100644 --- a/plugins/kapso/skills/integrate-whatsapp/scripts/lib/webhooks/kapso-api.js +++ b/plugins/kapso/skills/integrate-whatsapp/scripts/lib/webhooks/kapso-api.js @@ -10,46 +10,9 @@ function normalizeBaseUrl(raw) { return raw.replace(/\/+$/, ''); } -function isLocalhost(hostname) { - return hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '0.0.0.0' || hostname === '::1' || hostname === '[::1]'; -} - -function validateBaseUrl(baseUrl) { - let parsed; - try { - parsed = new URL(baseUrl); - } catch { - throw new Error('Invalid KAPSO_API_BASE_URL.'); - } - if (parsed.username || parsed.password) { - throw new Error('KAPSO_API_BASE_URL must not include credentials.'); - } - if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') { - throw new Error('KAPSO_API_BASE_URL must use http or https.'); - } - if (!process.env.KAPSO_API_ALLOW_LOCALHOST && isLocalhost(parsed.hostname)) { - throw new Error( - `KAPSO_API_BASE_URL points to localhost (${parsed.hostname}). ` + - 'Set KAPSO_API_ALLOW_LOCALHOST=true if this is intentional.' - ); - } - if ( - parsed.protocol === 'http:' && - !(process.env.KAPSO_API_ALLOW_LOCALHOST && isLocalhost(parsed.hostname)) && - process.env.KAPSO_API_ALLOW_INSECURE_HTTP !== 'true' - ) { - throw new Error( - 'KAPSO_API_BASE_URL must use https. ' + - 'Set KAPSO_API_ALLOW_INSECURE_HTTP=true only for trusted development hosts.' - ); - } -} - function kapsoConfigFromEnv() { - const baseUrl = normalizeBaseUrl(requireEnv('KAPSO_API_BASE_URL')); - validateBaseUrl(baseUrl); return { - baseUrl, + baseUrl: normalizeBaseUrl(requireEnv('KAPSO_API_BASE_URL')), apiKey: requireEnv('KAPSO_API_KEY') }; } diff --git a/plugins/kapso/skills/integrate-whatsapp/scripts/list-platform-phone-numbers.mjs b/plugins/kapso/skills/integrate-whatsapp/scripts/list-platform-phone-numbers.mjs index 8b1ac8b..6a33749 100644 --- a/plugins/kapso/skills/integrate-whatsapp/scripts/list-platform-phone-numbers.mjs +++ b/plugins/kapso/skills/integrate-whatsapp/scripts/list-platform-phone-numbers.mjs @@ -32,44 +32,8 @@ function normalizePlatformBase(raw) { return trimmed; } -function isLocalhost(hostname) { - return hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '0.0.0.0' || hostname === '::1' || hostname === '[::1]'; -} - -function validateBaseUrl(baseUrl) { - let parsed; - try { - parsed = new URL(baseUrl); - } catch { - throw new Error('Invalid KAPSO_API_BASE_URL.'); - } - if (parsed.username || parsed.password) { - throw new Error('KAPSO_API_BASE_URL must not include credentials.'); - } - if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') { - throw new Error('KAPSO_API_BASE_URL must use http or https.'); - } - if (!process.env.KAPSO_API_ALLOW_LOCALHOST && isLocalhost(parsed.hostname)) { - throw new Error( - `KAPSO_API_BASE_URL points to localhost (${parsed.hostname}). ` + - 'Set KAPSO_API_ALLOW_LOCALHOST=true if this is intentional.' - ); - } - if ( - parsed.protocol === 'http:' && - !(process.env.KAPSO_API_ALLOW_LOCALHOST && isLocalhost(parsed.hostname)) && - process.env.KAPSO_API_ALLOW_INSECURE_HTTP !== 'true' - ) { - throw new Error( - 'KAPSO_API_BASE_URL must use https. ' + - 'Set KAPSO_API_ALLOW_INSECURE_HTTP=true only for trusted development hosts.' - ); - } -} - function buildUrl(path, query) { const baseUrl = normalizePlatformBase(requireEnv('KAPSO_API_BASE_URL')); - validateBaseUrl(baseUrl); const cleanedPath = path.replace(/^\/+/, ''); const url = new URL(`${baseUrl}/platform/v1/${cleanedPath}`); @@ -161,3 +125,4 @@ async function main() { } main().then((code) => process.exit(code)); + diff --git a/plugins/kapso/skills/observe-whatsapp/scripts/lib/messages/kapso-api.js b/plugins/kapso/skills/observe-whatsapp/scripts/lib/messages/kapso-api.js index f71e5b3..caf14de 100644 --- a/plugins/kapso/skills/observe-whatsapp/scripts/lib/messages/kapso-api.js +++ b/plugins/kapso/skills/observe-whatsapp/scripts/lib/messages/kapso-api.js @@ -10,46 +10,9 @@ function normalizeBaseUrl(raw) { return raw.replace(/\/+$/, ''); } -function isLocalhost(hostname) { - return hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '0.0.0.0' || hostname === '::1' || hostname === '[::1]'; -} - -function validateBaseUrl(baseUrl) { - let parsed; - try { - parsed = new URL(baseUrl); - } catch { - throw new Error('Invalid KAPSO_API_BASE_URL.'); - } - if (parsed.username || parsed.password) { - throw new Error('KAPSO_API_BASE_URL must not include credentials.'); - } - if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') { - throw new Error('KAPSO_API_BASE_URL must use http or https.'); - } - if (!process.env.KAPSO_API_ALLOW_LOCALHOST && isLocalhost(parsed.hostname)) { - throw new Error( - `KAPSO_API_BASE_URL points to localhost (${parsed.hostname}). ` + - 'Set KAPSO_API_ALLOW_LOCALHOST=true if this is intentional.' - ); - } - if ( - parsed.protocol === 'http:' && - !(process.env.KAPSO_API_ALLOW_LOCALHOST && isLocalhost(parsed.hostname)) && - process.env.KAPSO_API_ALLOW_INSECURE_HTTP !== 'true' - ) { - throw new Error( - 'KAPSO_API_BASE_URL must use https. ' + - 'Set KAPSO_API_ALLOW_INSECURE_HTTP=true only for trusted development hosts.' - ); - } -} - function kapsoConfigFromEnv() { - const baseUrl = normalizeBaseUrl(requireEnv('KAPSO_API_BASE_URL')); - validateBaseUrl(baseUrl); return { - baseUrl, + baseUrl: normalizeBaseUrl(requireEnv('KAPSO_API_BASE_URL')), apiKey: requireEnv('KAPSO_API_KEY') }; } diff --git a/plugins/kapso/skills/observe-whatsapp/scripts/lib/status/kapso-api.js b/plugins/kapso/skills/observe-whatsapp/scripts/lib/status/kapso-api.js index f78b062..07abd03 100644 --- a/plugins/kapso/skills/observe-whatsapp/scripts/lib/status/kapso-api.js +++ b/plugins/kapso/skills/observe-whatsapp/scripts/lib/status/kapso-api.js @@ -10,46 +10,9 @@ function normalizeBaseUrl(raw) { return raw.replace(/\/+$/, ''); } -function isLocalhost(hostname) { - return hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '0.0.0.0' || hostname === '::1' || hostname === '[::1]'; -} - -function validateBaseUrl(baseUrl) { - let parsed; - try { - parsed = new URL(baseUrl); - } catch { - throw new Error('Invalid KAPSO_API_BASE_URL.'); - } - if (parsed.username || parsed.password) { - throw new Error('KAPSO_API_BASE_URL must not include credentials.'); - } - if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') { - throw new Error('KAPSO_API_BASE_URL must use http or https.'); - } - if (!process.env.KAPSO_API_ALLOW_LOCALHOST && isLocalhost(parsed.hostname)) { - throw new Error( - `KAPSO_API_BASE_URL points to localhost (${parsed.hostname}). ` + - 'Set KAPSO_API_ALLOW_LOCALHOST=true if this is intentional.' - ); - } - if ( - parsed.protocol === 'http:' && - !(process.env.KAPSO_API_ALLOW_LOCALHOST && isLocalhost(parsed.hostname)) && - process.env.KAPSO_API_ALLOW_INSECURE_HTTP !== 'true' - ) { - throw new Error( - 'KAPSO_API_BASE_URL must use https. ' + - 'Set KAPSO_API_ALLOW_INSECURE_HTTP=true only for trusted development hosts.' - ); - } -} - function kapsoConfigFromEnv() { - const baseUrl = normalizeBaseUrl(requireEnv('KAPSO_API_BASE_URL')); - validateBaseUrl(baseUrl); return { - baseUrl, + baseUrl: normalizeBaseUrl(requireEnv('KAPSO_API_BASE_URL')), apiKey: requireEnv('KAPSO_API_KEY') }; } diff --git a/plugins/kapso/skills/observe-whatsapp/scripts/lib/triage/kapso-api.js b/plugins/kapso/skills/observe-whatsapp/scripts/lib/triage/kapso-api.js index f71e5b3..caf14de 100644 --- a/plugins/kapso/skills/observe-whatsapp/scripts/lib/triage/kapso-api.js +++ b/plugins/kapso/skills/observe-whatsapp/scripts/lib/triage/kapso-api.js @@ -10,46 +10,9 @@ function normalizeBaseUrl(raw) { return raw.replace(/\/+$/, ''); } -function isLocalhost(hostname) { - return hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '0.0.0.0' || hostname === '::1' || hostname === '[::1]'; -} - -function validateBaseUrl(baseUrl) { - let parsed; - try { - parsed = new URL(baseUrl); - } catch { - throw new Error('Invalid KAPSO_API_BASE_URL.'); - } - if (parsed.username || parsed.password) { - throw new Error('KAPSO_API_BASE_URL must not include credentials.'); - } - if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') { - throw new Error('KAPSO_API_BASE_URL must use http or https.'); - } - if (!process.env.KAPSO_API_ALLOW_LOCALHOST && isLocalhost(parsed.hostname)) { - throw new Error( - `KAPSO_API_BASE_URL points to localhost (${parsed.hostname}). ` + - 'Set KAPSO_API_ALLOW_LOCALHOST=true if this is intentional.' - ); - } - if ( - parsed.protocol === 'http:' && - !(process.env.KAPSO_API_ALLOW_LOCALHOST && isLocalhost(parsed.hostname)) && - process.env.KAPSO_API_ALLOW_INSECURE_HTTP !== 'true' - ) { - throw new Error( - 'KAPSO_API_BASE_URL must use https. ' + - 'Set KAPSO_API_ALLOW_INSECURE_HTTP=true only for trusted development hosts.' - ); - } -} - function kapsoConfigFromEnv() { - const baseUrl = normalizeBaseUrl(requireEnv('KAPSO_API_BASE_URL')); - validateBaseUrl(baseUrl); return { - baseUrl, + baseUrl: normalizeBaseUrl(requireEnv('KAPSO_API_BASE_URL')), apiKey: requireEnv('KAPSO_API_KEY') }; }