From d3790ab8569f75a850d641c48a0e71d5e80175ac Mon Sep 17 00:00:00 2001 From: yeuimu <2197651308@qq.com> Date: Wed, 5 Aug 2026 10:35:04 +0800 Subject: [PATCH] refactor: rename skill 'inkreach-teable-ops' -> 'inkreach-teable' Rename the skill directory and update all references (SKILL.md frontmatter name, AGENTS.md, design doc, install.sh). Git tracks this as a rename so history is preserved. --- .agents/skills/inkreach-teable/SKILL.md | 155 +++++++ .../api-reference/action-types.md | 246 +++++++++++ .../api-reference/field-types.md | 389 ++++++++++++++++++ .../api-reference/trigger-types.md | 236 +++++++++++ .../guides/automation-guide.md | 191 +++++++++ .../inkreach-teable/guides/cli-reference.md | 182 ++++++++ .../inkreach-teable/guides/development.md | 121 ++++++ .../inkreach-teable/guides/instance-access.md | 87 ++++ .../inkreach-teable/guides/tables-fields.md | 327 +++++++++++++++ .agents/skills/inkreach-teable/metadata.json | 16 + .../skills/inkreach-teable/scripts/install.sh | 73 ++++ docs/cli-skill-design.md | 8 +- 12 files changed, 2027 insertions(+), 4 deletions(-) create mode 100644 .agents/skills/inkreach-teable/SKILL.md create mode 100644 .agents/skills/inkreach-teable/api-reference/action-types.md create mode 100644 .agents/skills/inkreach-teable/api-reference/field-types.md create mode 100644 .agents/skills/inkreach-teable/api-reference/trigger-types.md create mode 100644 .agents/skills/inkreach-teable/guides/automation-guide.md create mode 100644 .agents/skills/inkreach-teable/guides/cli-reference.md create mode 100644 .agents/skills/inkreach-teable/guides/development.md create mode 100644 .agents/skills/inkreach-teable/guides/instance-access.md create mode 100644 .agents/skills/inkreach-teable/guides/tables-fields.md create mode 100644 .agents/skills/inkreach-teable/metadata.json create mode 100644 .agents/skills/inkreach-teable/scripts/install.sh diff --git a/.agents/skills/inkreach-teable/SKILL.md b/.agents/skills/inkreach-teable/SKILL.md new file mode 100644 index 0000000..5ea9cc3 --- /dev/null +++ b/.agents/skills/inkreach-teable/SKILL.md @@ -0,0 +1,155 @@ +--- +name: inkreach-teable +description: >- + Operate a deployed inkreach-teable instance (a Teable fork with a self-built + automation engine). Configure tables, fields, records, views, and automations + (8 triggers: recordCreated/recordUpdated/recordCondition/recordCreatedOrUpdated/ + formSubmitted/scheduledTime/buttonClick/webhook; 6 actions: script/sendEmail/ + httpRequest/createRecord/updateRecord/getRecords), plus secrets and execution + history. Trigger when the user wants to manage tables/fields/records/views, build + or configure automations/workflows, set up button-field triggers, schedule jobs, + create webhooks, inspect run history, or troubleshoot a deployed inkreach-teable + instance — even if they don't say "inkreach-teable" but reference Teable-style IDs + (bseXXX, tblXXX, fldXXX, recXXX, viwXXX, wflXXX) or ask to configure tables and + automation on the local/deployed instance. Also covers dev/deploy tasks (build, + restart systemd services, known gotchas). Uses the inkreach-teable CLI, which targets + the project's OWN automation contract (NOT the official teable CLI). +--- + +# inkreach-teable operations + +This skill operates a deployed inkreach-teable instance via the `inkreach-teable` CLI. +Respond in the user's language. Keep answers concise and action-oriented. + +> **Critical**: inkreach-teable's automation engine is **self-built and differs from +> official teable**. The official `teable` CLI does NOT work for automations here. +> Always use the `inkreach-teable` CLI. See [Key Constraints](#3-key-constraints) for the +> exact key-name differences. + +## 1. Prerequisites & Constraints + +- All operations use the `inkreach-teable` CLI. Install via `bash scripts/install.sh` (relative to this skill dir) if `inkreach-teable` is not found. +- **Endpoint + token**: configure once with `inkreach-teable auth login --endpoint --token `. Default endpoint is `http://localhost:3000`. Verify with `inkreach-teable auth status`. +- **Getting a PAT**: `inkreach-teable auth token create --name my-cli` (returns the full token **only once**). Scopes default to automation+table+record read/write. Or create via the web UI (Settings → Personal Access Tokens). +- **`--base-id`**: omit if a default was set via `auth login`; CLI uses it automatically. Ask user only if a command fails with MISSING_BASE_ID. +- **`--json` mode**: add `--json` to any command for raw JSON output (for agent parsing). +- **Unfamiliar commands**: `inkreach-teable --help` as a fallback. +- **Read the relevant guide before acting**: after entering a module, read the doc(s) marked "Required" in the module map. + +## 2. Module & Command Navigation + +### 2.1 Module Map + +| Module | What it solves | Entry commands | Guide to read | +|--------|---------------|----------------|---------------| +| Tables & Fields | Create/modify table structure | `table create`, `field create` | [tables-fields.md](guides/tables-fields.md), [field-types.md](api-reference/field-types.md) | +| Records | Read/write row data | `record get`, `record create/update/delete` | [cli-reference.md § record](guides/cli-reference.md#base--table--field--record--view) | +| Views | Persistent filtered/sorted perspectives | `view create/update` | [cli-reference.md § view](guides/cli-reference.md#base--table--field--record--view) | +| Automation | Event-driven workflows | `workflow *`, `trigger set`, `action add` | [automation-guide.md](guides/automation-guide.md), [trigger-types.md](api-reference/trigger-types.md), [action-types.md](api-reference/action-types.md) | +| Secrets | Store API keys for script actions | `secret set/list/delete` | [automation-guide.md § Secrets](guides/automation-guide.md#secrets) | +| Execution History | Inspect run results / rerun failures | `runs list/get/rerun` | [cli-reference.md § runs](guides/cli-reference.md#runs) | +| Manual Trigger | Test by firing button-click/webhook | `fire button-click`, `fire webhook` | [automation-guide.md § Testing](guides/automation-guide.md#testing--triggering) | +| SQL | Read-only analytics, JOINs, aggregation | `sql ` | [cli-reference.md § sql](guides/cli-reference.md#sql) | +| Dev/Deploy | Build, restart services, known gotchas | (systemd / pnpm build) | [development.md](guides/development.md) | + +### 2.2 Routing Rules + +> **Before executing**: read the documents marked "Required" in the guide for the module. + +| User intent | Correct module | Do NOT do this | +|-------------|---------------|----------------| +| Configure an automation | Automation: `workflow create` → `trigger set` → `action add` → `activate` | Manually editing DB rows | +| React to new records | Trigger `recordCreated` | Polling loop in a script | +| React to field changes | Trigger `recordUpdated` with `--field-ids` | `recordCondition` (ignores fields) | +| React to a condition matching | Trigger `recordCondition` (NOT recordMatchesConditions) | Official-teable key names | +| Button click starts a workflow | Trigger `buttonClick` + a Button-type field | Any other trigger type | +| Receive external HTTP | Trigger `webhook`; fire via `fire webhook` | Expecting auth (backend ignores it) | +| Schedule recurring job | Trigger `scheduledTime` (only type+interval work) | Passing triggerTime/weekdays (backend ignores) | +| Per-row AI / transformation | Action `script` reading `triggerInput` | Manual row-by-row via record API | +| Send notification | Action `sendEmail` or `httpRequest` | — | +| Read records for later writes | `record get` (returns record IDs) | `sql` (no record IDs returned) | +| Analytics / aggregation / JOIN | `sql` with GROUP BY | Fetch all records + compute in code | +| Store an API key for a script | `secret set` → read in script via `process.env.NAME` | Hardcoding in script code | +| Inspect why a workflow didn't run | `runs list --status failed` → `runs get` | Guessing; re-activating blindly | + +### 2.3 Quick Syntax + +```bash +# Configure access (once) +inkreach-teable auth login --endpoint http://localhost:3000 --token + +# Create a table +inkreach-teable table create --name "Tasks" --base-id bseXXX \ + --fields '[{"name":"Title","type":"singleLineText"},{"name":"Status","type":"singleSelect","options":{"choices":["Todo","Done"]}}]' + +# Create a workflow + set buttonClick trigger + add a script action + activate +inkreach-teable workflow create --name "On Go click" --base-id bseXXX +inkreach-teable trigger set wflXXX --type buttonClick --table-id tblXXX --field-ids '["fldXXX"]' +inkreach-teable action add wflXXX --type script --code 'return triggerInput.record.fields' +inkreach-teable workflow activate wflXXX + +# Test by firing the button +inkreach-teable fire button-click tblXXX recXXX fldXXX +inkreach-teable runs list wflXXX +``` + +For complete syntax and all options, read [cli-reference.md](guides/cli-reference.md). + +## 3. Key Constraints + +These differ from official teable and from the (stale) `docs/automation/triggers.md`. **Use these exact values:** + +1. **Filter key is `conditions`** (NOT `filter`). Pass via `trigger set --conditions ''`. The JSON shape is `{conjunction:"and"|"or", filterSet:[{fieldId, operator, value}]}`. +2. **Watch-fields key is `fieldIds`** (NOT `watchFieldIds`). For `recordUpdated`/`buttonClick`. `[]` means watch all fields. +3. **Trigger category is `recordCondition`** (NOT `recordMatchesConditions`). It triggers on create+update when the record matches conditions. +4. **`scheduledTime` backend honors only `timing.type` + `timing.interval`**. `triggerTime`, `weekdays`, `daysOfMonth`, `starting`, `ending`, `tz` are all IGNORED by the backend. `weeks` type is hardcoded to every Sunday. Don't configure fields the backend won't use. +5. **Webhook real route is `/api/webhook/{baseId}/{workflowId}`** (no `/base/` or `/workflow/` path segments), and `authorization` config is UI-only — **backend does NOT validate it**. Fire via `inkreach-teable fire webhook `. +6. **Node updates use whole-graph replacement** (`PUT /node`). CLI commands (`trigger set`, `action add/update/delete`) handle GET→modify→PUT internally — always use them, never hand-edit partial graphs. +7. **`activate` creates a snapshot**: changes to nodes only take effect after re-activating. `hasDraft: true` means unactivated changes exist. +8. **`workflow delete` is a SOFT delete** (cascades to base_node, auto-token, trigger unregister). Test workflows should be `deactivate`d, not deleted. +9. **Secrets values are write-only** — `secret list` never returns the value. Scripts read them via `process.env.`. +10. **PAT format is `teable_{id}_{encryptedSign}`** — create via `auth token create`, the full string is returned **once**. +11. **Script sandbox has NO `fs`/`eval`/`Function`**, but has `triggerInput`, `console`, `fetch`, `process.env` (= secrets + `AUTOMATION_TOKEN` + `PUBLIC_ORIGIN`), and a controlled `require`. Use `triggerInput._accessToken` + `triggerInput._apiBaseUrl` to call back into the instance API. + +## 4. Execution Rules + +### 4.1 Standard Order + +1. **Confirm context** — identify target base/table/workflow (`--base-id`, IDs in user's message). +2. **Read before write** — `table get` / `field list` / `workflow get` to confirm current state. +3. **Execute changes** — create/update via CLI commands. +4. **Verify** — re-read; for automations, `activate` then `runs list` after a test fire. + +### 4.2 Critical Rules (with reasoning) + +1. **Use `inkreach-teable` CLI, never `teable` CLI for automations** — official CLI uses incompatible contract (filter/watchFieldIds/recordMatchesConditions) and will 404 or corrupt config. +2. **Read [trigger-types.md](api-reference/trigger-types.md) before configuring a trigger** — contains exact config keys per trigger; guessing leads to silent misfires. +3. **Build the full chain then activate** — `workflow create` → `trigger set` → `action add` → `activate`. Editing nodes after activation requires re-`activate` to snapshot. +4. **For `buttonClick`, ensure the field is a Button field bound to this workflow** — create via `field create --type button` with `options.workflowId`, or it won't render/click. Verify `workflow.isActive` (the field options capture a stale `isActive` snapshot at creation). +5. **Read [action-types.md](api-reference/action-types.md) before writing scripts** — covers sandbox globals, variable references `{{nodeIndex.path}}`, and the `_accessToken`/`_apiBaseUrl` back-channel. + +## 5. Common Errors & Recovery + +| Symptom | Cause | Fix | +|---------|-------|-----| +| `MISSING_BASE_ID` | No base id resolved | Pass `--base-id`, or `auth login --base-id ` | +| `401` / "access denied" | Token invalid/expired or lacks scope | `auth status`; recreate PAT with needed scopes | +| `403` | Base role lacks permission | User must grant role (owner/creator) in web UI | +| Workflow never fires | Not activated, or trigger misconfigured | `workflow get` → check `isActive`; verify trigger type + tableId/fieldIds | +| `scheduledTime` fires at wrong time | Backend ignores most timing fields | Only `timing.type`+`interval` work; use cron-friendly intervals | +| button-click returns 400 "not active" | Workflow deactivated since field created | `workflow activate `, or field options are stale | +| `runs list` shows `failed` | Action error | `runs get ` → read `nodeResults[].error` | +| Secret not readable in script | Wrong env var name | Scripts use `process.env.` where NAME = secret name | + +## 6. API Reference Index + +Files in `api-reference/`, read when you need exact config formats: + +- **[trigger-types.md](api-reference/trigger-types.md)** — all 8 triggers: type string, config keys, output variables. +- **[action-types.md](api-reference/action-types.md)** — all 6 actions: config keys, sandbox globals, variable references. +- **[field-types.md](api-reference/field-types.md)** — Teable field types, aliases, and option shapes (incl. Button field with `workflowId`). + +## 7. Instance Access + +Endpoint, service status, and credential-acquisition steps: see [instance-access.md](guides/instance-access.md). +For dev/deploy (build, restart systemd, known gotchas): see [development.md](guides/development.md). diff --git a/.agents/skills/inkreach-teable/api-reference/action-types.md b/.agents/skills/inkreach-teable/api-reference/action-types.md new file mode 100644 index 0000000..9f31ffe --- /dev/null +++ b/.agents/skills/inkreach-teable/api-reference/action-types.md @@ -0,0 +1,246 @@ +# Action Types Reference + +All 6 actions of the inkreach-teable automation engine. Actions run in topological order after the trigger; each receives the merged upstream output. + +An action node: +```json +{ + "id": "node-2", + "type": "action", + "category": "", + "config": { ... }, + "name": null +} +``` + +Use `inkreach-teable action add --type ...` (don't hand-build the JSON). + +--- + +## Sandbox Globals (all script actions) + +Inside a `script` action, these are available: + +| Global | Description | +|--------|-------------| +| `triggerInput` | Merged output of trigger + all upstream actions. Contains `_accessToken`, `_apiBaseUrl`, `_baseId`, `_workflowId`, `_nodeId`, `_secrets`, `_publicOrigin` | +| `console` | Captured to execution logs (`logs.stdout` / `logs.stderr`) | +| `fetch` | Standard fetch (30s timeout recommended) | +| `process.env` | Secrets (by name) + `AUTOMATION_TOKEN` + `PUBLIC_ORIGIN` | +| `require` | Controlled — allows whitelisted npm deps declared via `dependencies` | + +**NOT available**: `fs`, `eval`, `Function`, `process` (only `process.env`), `child_process`. + +**Back-channel API access**: inside scripts, call the instance's own API: +```js +const res = await fetch(triggerInput._apiBaseUrl + '/api/table/tblXXX/record', { + headers: { Authorization: 'Bearer ' + triggerInput._accessToken } +}); +``` +`_accessToken` is a 1-hour JWT auto-created on activation (scope: table|read + record|read/create/update). + +--- + +## 1. script — Run JavaScript + +```bash +inkreach-teable action add --type script \ + --code 'return { doubled: triggerInput.record.fields.Amount * 2 }' +# or from a file +inkreach-teable action add --type script --code-file ./my-script.js --timeout 120000 +``` + +**Config:** +```json +{ + "code": "// JS code", + "timeout": 60000, + "dependencies": [ { "name": "axios", "version": "1.6.0" } ] +} +``` +- `timeout`: default 60000 (120000 in batch mode). +- `dependencies`: optional npm packages; `require('axios')` works in-sandbox. + +**Output:** +```json +{ "result": "", "logs": { "stdout": "...", "stderr": "..." }, "executionTime": 123 } +``` + +In batch mode (`triggerInput.records` array): +```js +const records = triggerInput.records || [triggerInput.record]; +const results = await Promise.allSettled(records.map(async (r) => { + // per-record work +})); +return { ok: results.filter(r => r.status === 'fulfilled').length }; +``` + +--- + +## 2. sendEmail — Send an email + +```bash +inkreach-teable action add --type sendEmail \ + --to user@example.com --subject 'Alert' --body '

Check this

' +``` + +**Config:** +```json +{ + "to": "user@example.com", + "subject": "Alert", + "body": "...", + "transporterName": "automation" +} +``` +- `transporterName`: `automation` (default) or `notify`. Requires SMTP configured on the server. + +**Output:** `{ "sent": true, "to": "..." }` + +Variable reference: `--body '{{1.record.fields.Name}}'` injects upstream data. + +--- + +## 3. httpRequest — Call an external API + +```bash +inkreach-teable action add --type httpRequest \ + --url https://api.example.com/webhook --method POST \ + --headers '{"X-Api-Key":"xxx"}' --req-body '{"event":"test"}' +``` + +**Config:** +```json +{ + "url": "https://...", + "method": "POST", + "headers": { "X-Api-Key": "xxx" }, + "body": { ... } +} +``` +- `method`: GET (default) | POST | PUT | PATCH | DELETE. +- 30s hard timeout. +- GET/HEAD do not send a body. + +**Output:** `{ "statusCode": 200, "headers": {...}, "body": }` +- Non-2xx still returns output but the node status is `failed`. + +--- + +## 4. createRecord — Create records + +```bash +inkreach-teable action add --type createRecord \ + --target-table tblXXX \ + --records '[{"fields":{"Name":"New","Source":"{{1.record.id}}"}}]' +``` + +**Config:** +```json +{ + "tableId": "tblXXX", + "fieldKeyType": "id", + "records": [ { "fields": { "fldName": "value" } } ] +} +``` +- `fieldKeyType`: `id` (default) or `name`. + +**Output:** `{ "records": [ { "id": "recXXX", "fields": {...} } ] }` + +--- + +## 5. updateRecord — Update records + +```bash +inkreach-teable action add --type updateRecord \ + --target-table tblXXX \ + --records '[{"id":"recXXX","fields":{"Status":"Done"}}]' +``` + +**Config:** +```json +{ + "tableId": "tblXXX", + "fieldKeyType": "id", + "records": [ { "id": "recXXX", "fields": { "fldStatus": "Done" } } ] +} +``` +- Each record **must** include `id` (recordId). + +**Output:** `{ "records": [ ... ] }` + +--- + +## 6. getRecords — Query records + +```bash +inkreach-teable action add --type getRecords \ + --target-table tblXXX \ + --query '{"pageSize":100,"filter":{"conjunction":"and","filterSet":[]}}' +``` + +**Config:** +```json +{ + "tableId": "tblXXX", + "query": { + "pageSize": 100, + "page": 1, + "filter": { "conjunction": "and", "filterSet": [...] }, + "orderBy": "fldXXX", + "search": "keyword" + } +} +``` +- `search` string is auto-normalized to the `search[]` tuple format the API expects. + +**Output:** `{ "records": [...], "total": 123 }` + +--- + +## Variable References + +In any action's **string** config field, use `{{nodeIndex.path}}` to inject upstream output: + +| Reference | Resolves to | +|-----------|-------------| +| `{{1.record.id}}` | trigger record id (trigger = node 1) | +| `{{1.record.fields.fldStatus}}` | trigger record's Status field | +| `{{2.result}}` | script action's return value (node 2) | +| `{{3.records}}` | getRecords output array (node 3) | +| `{{2.statusCode}}` | httpRequest status (node 2) | + +**Node index** = topological execution order (trigger is always 1, then actions in chain order). + +Script actions can also read everything via `triggerInput` (the merged upstream output): +```js +const prev = triggerInput; // has all upstream fields merged +``` + +## Common Action Patterns + +### Call OpenAI with a secret +```js +// secret named OPENAI_KEY must be set first +const res = await fetch('https://api.openai.com/v1/chat/completions', { + method: 'POST', + headers: { Authorization: 'Bearer ' + process.env.OPENAI_KEY, 'Content-Type': 'application/json' }, + body: JSON.stringify({ + model: 'gpt-4o-mini', + messages: [{ role: 'user', content: triggerInput.record.fields.Prompt }] + }) +}); +const data = await res.json(); +return { reply: data.choices[0].message.content }; +``` + +### Conditional logic +```js +const status = triggerInput.record.fields.Status; +if (status === 'Done') { + // call API + return { skipped: false }; +} +return { skipped: true }; +``` +(There's no dedicated "if" node — do branching inside a script.) diff --git a/.agents/skills/inkreach-teable/api-reference/field-types.md b/.agents/skills/inkreach-teable/api-reference/field-types.md new file mode 100644 index 0000000..db346bd --- /dev/null +++ b/.agents/skills/inkreach-teable/api-reference/field-types.md @@ -0,0 +1,389 @@ +# Field Types Reference + +Complete reference of all Teable field types, aliases, option shapes, and smart inference rules. + +--- + +## Type Overview + +| `--type` value | Display name | Purpose | Requires `--options` | +|---------------|-------------|---------|---------------------| +| `singleLineText` | Single Line Text | Short text (titles, names, IDs) | No | +| `longText` | Long Text | Rich text, descriptions, notes | No | +| `number` | Number | Integers or decimals | No (defaults to integer) | +| `singleSelect` | Single Select | Pick one from fixed options | Yes (`choices`) | +| `multipleSelect` | Multiple Select | Pick many (tags/labels) | Yes (`choices`) | +| `date` | Date | Date or datetime | No | +| `checkbox` | Checkbox | Yes/no toggle | No | +| `rating` | Rating | Star/numeric rating 1..10 | No (defaults: max=5, star, yellow) | +| `attachment` | Attachment | Files, images, documents | No | +| `user` | User | Person/collaborator reference | No | +| `link` | Link | Reference records in another table | Yes (`foreignTableId`/`foreignTableName`) | +| `lookup` | Lookup | Mirror a field from linked record | Yes (`lookupOptions`) | +| `rollup` | Rollup | Aggregate linked records (SUM, COUNT, etc.) | Yes (expression + `lookupOptions`) | +| `conditionalRollup` | Conditional Rollup | Aggregate from ANY table (no link required) | Yes (expression + `lookupOptions` + conditions) | +| `formula` | Formula | Per-row computation | Yes (`expression`) | +| `createdTime` | Created Time | Auto-set when record created | No | +| `lastModifiedTime` | Last Modified Time | Auto-updated on any change | No | +| `createdBy` | Created By | Auto-set to creator's user | No | +| `lastModifiedBy` | Last Modified By | Auto-updated to last editor | No | +| `autoNumber` | Auto Number | Auto-incrementing integer | No | +| `button` | Button | Click-to-trigger automation | Yes (`workflow`; see Button section) | + +### Date vs DateTime + +There is no separate `dateTime` type. Use `date` and control the time component via `--options`: + +```bash +# Date-only +--options '{"formatting":{"date":"YYYY-MM-DD","time":"None","timeZone":"UTC"}}' + +# DateTime (with time) +--options '{"formatting":{"date":"YYYY-MM-DD","time":"HH:mm","timeZone":"America/New_York"}}' +``` + +| `time` value | Behavior | +|-------------|----------| +| `"None"` | Date only (no time picker) | +| `"HH:mm"` | 24-hour time | +| `"hh:mm A"` | 12-hour time with AM/PM | + +--- + +## Type Aliases + +When using `field create --type ` or `table create --fields` compact format: + +| Alias | Full type | +|-------|-----------| +| `text`, `str` | `singleLineText` | +| `long` | `longText` | +| `num`, `int` | `number` | +| `check`, `bool` | `checkbox` | +| `sel`, `select` | `singleSelect` | +| `multi`, `tags` | `multipleSelect` | +| `date` | `date` | +| `rate`, `star` | `rating` | +| `user`, `person` | `user` | +| `file`, `attach` | `attachment` | +| `link`, `rel` | `link` | +| `rollup` | `rollup` | +| `condRollup` | `conditionalRollup` | +| `formula` | `formula` | +| `auto` | `autoNumber` | +| `created` | `createdTime` | +| `modified` | `lastModifiedTime` | +| `createdBy` | `createdBy` | +| `modifiedBy` | `lastModifiedBy` | +| `button` | `button` | + +**`table create --fields` compact format** also supports inline select options: + +| Format | Meaning | +|--------|---------| +| `"Name:text"` | Single line text field | +| `"Amount:num"` | Number field | +| `"Status:sel:Todo,Doing,Done"` | Single select with choices | +| `"Tags:mul:Bug,Feature,UI"` | Multi-select with choices | +| `"Done:check"` | Checkbox | +| `"Score:rate"` | Rating (defaults) | + +--- + +## Option Shapes by Type + +### SingleSelect / MultipleSelect + +```bash +# Simple — strings only, colors auto-assigned +inkreach-teable field create --table-id tblXXX --name "Status" --type singleSelect \ + --options '{"choices":["Todo","Doing","Done"]}' + +# With explicit colors +inkreach-teable field create --table-id tblXXX --name "Priority" --type singleSelect \ + --options '{"choices":[{"name":"High","color":"redBright"},{"name":"Low","color":"gray"}]}' + +# With default value +inkreach-teable field create --table-id tblXXX --name "Status" --type singleSelect \ + --options '{"choices":["Todo","Done"],"defaultValue":"Todo"}' +``` + +| Option key | Type | Required | Notes | +|-----------|------|----------|-------| +| `choices` | `string[]` or `{name,color}[]` | yes | Colors auto-assigned from palette if omitted | +| `defaultValue` | `string` (singleSelect) / `string[]` (multipleSelect) | no | Must match a choice name | +| `preventAutoNewOptions` | `boolean` | no | Disable adding choices from cell editing | + +**Available colors**: `blue`, `cyan`, `gray`, `green`, `orange`, `pink`, `purple`, `red`, `teal`, `yellow` — each with `Light2`, `Light1`, `Bright`, (base), `Dark1` variants. When colors are auto-assigned, the system cycles through a built-in palette. + +### Number + +```bash +# Integer (default — no options needed) +inkreach-teable field create --table-id tblXXX --name "Count" --type number + +# Decimal with 2 places +inkreach-teable field create --table-id tblXXX --name "Price" --type number \ + --options '{"formatting":{"precision":2}}' + +# With default value +inkreach-teable field create --table-id tblXXX --name "Quantity" --type number \ + --options '{"defaultValue":1}' +``` + +| Option key | Type | Default | Notes | +|-----------|------|---------|-------| +| `formatting.precision` | `number` | `0` | Decimal places (0 = integer) | +| `formatting.type` | `"decimal"` / `"integer"` | `"decimal"` | Number type | +| `showAs` | object | — | Display as percentage, currency, etc. | +| `defaultValue` | `number` | — | Numeric default for new records | + +### Rating + +```bash +# Quick: default (max=5, star icon, yellow) +inkreach-teable field create --table-id tblXXX --name "Score" --type rating + +# Custom +inkreach-teable field create --table-id tblXXX --name "Priority" --type rating \ + --options '{"max":3,"icon":"flame","color":"redBright"}' + +# Max 10 +inkreach-teable field create --table-id tblXXX --name "NPS" --type rating \ + --options '{"max":10,"icon":"star","color":"tealBright"}' +``` + +| Option key | Type | Default | Notes | +|-----------|------|---------|-------| +| `max` | `number` | `5` | 1–10 | +| `icon` | `"star"` / `"moon"` / `"sun"` / `"zap"` / `"flame"` / `"heart"` / `"apple"` / `"thumb-up"` | `"star"` | Icon to display | +| `color` | `"yellowBright"` / `"redBright"` / `"tealBright"` | `"yellowBright"` | Icon fill color | + +### Date + +```bash +# Date only (default) +inkreach-teable field create --table-id tblXXX --name "Due" --type date + +# Date with time +inkreach-teable field create --table-id tblXXX --name "Appointment" --type date \ + --options '{"formatting":{"date":"YYYY-MM-DD","time":"HH:mm","timeZone":"Asia/Shanghai"}}' + +# Auto-fill with "now" for new records +inkreach-teable field create --table-id tblXXX --name "Created At" --type date \ + --options '{"defaultValue":"now"}' +``` + +| Option key | Type | Default | Notes | +|-----------|------|---------|-------| +| `formatting.date` | `string` | `"YYYY-MM-DD"` | Preset: `M/D/YYYY`, `D/M/YYYY`, `YYYY/MM/DD`, `YYYY-MM-DD`, `YYYY-MM`, `MM-DD`, `YYYY`, `MM`, `DD` | +| `formatting.time` | `"None"` / `"HH:mm"` / `"hh:mm A"` | `"None"` | Time picker if not `"None"` | +| `formatting.timeZone` | `string` | System local | IANA timezone name | +| `defaultValue` | `"now"` | — | Auto-fills current time on new records | + +### Link + +```bash +# Using table name (preferred) +inkreach-teable field create --table-id tblXXX --name "Project" --type link \ + --options '{"foreignTableName":"Projects","relationship":"manyOne"}' + +# Using table ID +inkreach-teable field create --table-id tblXXX --name "Project" --type link \ + --options '{"foreignTableId":"tblXXX","relationship":"manyOne"}' + +# Many-to-many +inkreach-teable field create --table-id tblXXX --name "Tags" --type link \ + --options '{"foreignTableName":"Tags","relationship":"manyMany"}' +``` + +| Option key | Type | Required | Notes | +|-----------|------|----------|-------| +| `foreignTableName` | `string` | * | Resolved to `foreignTableId` by CLI; preferred over ID | +| `foreignTableId` | `string` | * | Direct ID; use `foreignTableName` instead | +| `relationship` | `"oneOne"` / `"oneMany"` / `"manyOne"` / `"manyMany"` | yes | Direction is from THIS table to the FOREIGN table | +| `isOneWay` | `boolean` | no | No symmetric field in foreign table | +| `filterByViewId` | `string` | no | Limit selectable records to a view | +| `visibleFieldIds` | `string[]` | no | Fields shown in linked record picker | + +**Relationship quick reference**: + +| Relationship | Meaning | Example | +|-------------|---------|---------| +| `manyOne` | Many records here → one record there | Tasks → Project | +| `oneMany` | One record here → many records there | Project → Tasks | +| `oneOne` | One-to-one | User ↔ Profile | +| `manyMany` | Many-to-many | Tasks ↔ Tags | + +> **Name resolution**: use `foreignTableName` instead of `foreignTableId` — the CLI resolves it. Preferred over manual ID lookups. + +### Formula + +```bash +inkreach-teable field create --table-id tblXXX --name "Full Name" --type formula \ + --options '{"expression":"{fldFirstName} & \" \" & {fldLastName}"}' + +inkreach-teable field create --table-id tblXXX --name "Total" --type formula \ + --options '{"expression":"{fldQuantity} * {fldPrice}"}' +``` + +| Option key | Type | Required | Notes | +|-----------|------|----------|-------| +| `expression` | `string` | yes | Formula referencing fields by ID: `{fldXXX}` | +| `timeZone` | `string` | no | For date functions | +| `formatting` | object | no | Formatting for result (number/date) | + +**Field references**: use `{fldXXX}` with field IDs. Get IDs from `field list `. + +### Rollup + +```bash +# Requires an existing link field + a target field in the linked table +inkreach-teable field create --table-id tblXXX --name "Total Hours" --type rollup \ + --options '{"expression":"sum({values})"}' \ + --lookup-options '{"linkFieldName":"Tasks","lookupFieldName":"Hours","foreignTableId":"tblTasks"}' +``` + +| Required | Meaning | +|----------|---------| +| `--options.expression` | Aggregation: `sum({values})`, `count({values})`, `average({values})`, `max({values})`, `min({values})`, `concatenate({values})`, `unique({values})`, `countDistinct({values})` | +| `--lookup-options.linkFieldName` | The link field in THIS table pointing to the foreign table | +| `--lookup-options.lookupFieldName` | The field in the FOREIGN table to aggregate | +| `--lookup-options.foreignTableId` | The foreign table (typically same as the link field's target) | + +**Name resolution**: `linkFieldName` and `lookupFieldName` accept field display names — CLI resolves them to IDs. + +### Lookup + +```bash +# Mirror a field from a linked record +inkreach-teable field create --table-id tblXXX --name "Project Name" --type singleLineText \ + --is-lookup \ + --lookup-options '{"linkFieldName":"Project","lookupFieldName":"Name","foreignTableId":"tblProjects"}' +``` + +Lookup uses the base type of the field being mirrored (here copied as `singleLineText`), with `--is-lookup` and `--lookup-options`. + +### ConditionalRollup + +```bash +# Aggregate from ANY table without a link field +inkreach-teable field create --table-id tblXXX --name "Active Tasks" --type conditionalRollup \ + --options '{"expression":"sum({values})"}' \ + --lookup-options '{"lookupFieldName":"Estimate","foreignTableId":"tblTasks"}' \ + --conditions '{"conjunction":"and","filterSet":[{"fieldId":"fldStatus","operator":"isNot","value":"Done"}]}' +``` + +### Button + +```bash +# Create a button field bound to a workflow +inkreach-teable field create --table-id tblXXX --name "Approve" --type button \ + --options '{"label":"Approve","color":"blue","workflow":{"id":"wflXXX","name":"Approval Flow"}}' + +# With click limit +inkreach-teable field create --table-id tblXXX --name "Run Once" --type button \ + --options '{"label":"Run","color":"teal","maxCount":1,"workflow":{"id":"wflXXX"}}' + +# With confirmation dialog +inkreach-teable field create --table-id tblXXX --name "Delete" --type button \ + --options '{"label":"Delete","color":"red","workflow":{"id":"wflXXX"},"confirm":{"title":"Are you sure?","description":"This cannot be undone","confirmText":"Yes, delete"}}' +``` + +| Option key | Type | Required | Default | Notes | +|-----------|------|----------|---------|-------| +| `label` | `string` | no | `"Button"` | Display text on the button | +| `color` | Color enum | no | `"teal"` | Button color | +| `workflow.id` | `string` | **yes** | — | Workflow ID triggered on click | +| `workflow.name` | `string` | no | — | Workflow display name (informational) | +| `workflow.isActive` | `boolean` | no | — | Captured at creation time; reflects whether workflow was active | +| `maxCount` | `number` | no | — | Max clicks per record; empty = unlimited | +| `resetCount` | `boolean` | no | — | Reset click count behavior | +| `confirm.title` | `string` | no | — | Confirmation dialog title | +| `confirm.description` | `string` | no | — | Confirmation dialog body | +| `confirm.confirmText` | `string` | no | — | Confirm button label | + +**Available button colors**: `blueLight2`, `blueLight1`, `blueBright`, `blue`, `blueDark1`, `cyan`, `cyanBright`, `cyanDark1`, `gray`, `grayBright`, `grayDark1`, `green`, `greenBright`, `greenDark1`, `orange`, `orangeBright`, `orangeDark1`, `pink`, `pinkBright`, `pinkDark1`, `purple`, `purpleBright`, `purpleDark1`, `red`, `redBright`, `redDark1`, `teal`, `tealBright`, `tealDark1`, `yellow`, `yellowBright`, `yellowDark1`. + +**Key behaviors**: + +1. **Workflow binding**: button clicks fire the `buttonClick` trigger of the bound workflow. The workflow MUST be active (`inkreach-teable workflow activate wflXXX`) for the button to be clickable. +2. **`workflow.isActive` snapshot**: captured when the field is created. If the workflow is deactivated later, the field's `isActive` goes stale — the button won't be clickable. Resolve by updating the field options or re-activating the workflow. +3. **Billing**: in this self-hosted CE fork, button fields are **always available** — billing gate is lifted. +4. **Cell value**: read-only `{"count": N}` tracking click count. Automatically incremented by the backend. +5. **Click limit**: when `maxCount` is set and count reaches it, button becomes disabled. + +--- + +## Smart Inference + +### Type inference from `--name` + +When `--type` is omitted, the CLI infers the type from the field name: + +| Name pattern | Inferred type | +|-------------|---------------| +| `status`, `priority`, `category`, `type`, `stage`, `level` | `singleSelect` | +| `tag`, `label` | `multipleSelect` | +| `description`, `notes`, `comment`, `content`, `body` | `longText` | +| `count`, `amount`, `price`, `quantity`, `total`, `number`, `age` | `number` | +| `rating`, `score`, `stars` | `rating` | +| `is*`, `has*`, `active`, `enabled`, `done`, `completed` | `checkbox` | +| `assignee`, `owner`, `user` | `user` | +| `attachment`, `file`, `image`, `photo`, `document` | `attachment` | +| `email`, `url` | `singleLineText` | +| `created time`, `created` (without "by") | `createdTime` | +| `last modified`, `updated` (without "by") | `lastModifiedTime` | +| `created by`, `author` | `createdBy` | +| `modified by`, `updated by` | `lastModifiedBy` | + +### Options inference + +| If you pass | Type inferred | Notes | +|-------------|--------------|-------| +| `--options '{"choices":["A","B"]}'` (no `--type`) | `singleSelect` | Colors auto-assigned | +| `--options '{"max":5}'` (no `--type`) | `rating` | Icon `star`, color `yellowBright` defaults applied | +| `--options '{"foreignTableName":"X"}'` (no `--type`) | `link` | | +| `--options '{"expression":"..."}'` (no `--type`) | `formula` | Unless combined with `foreignTableId` + conditions → `conditionalRollup` | +| `--options '{"workflow":{"id":"wflXXX"}}'` (no `--type`) | `button` | | +| No `--type`, no `--options` | Depot on `--name` pattern | Falls back to `singleLineText` if no match | + +### Options defaults + +| Type | Defaults applied when omitted | +|------|------------------------------| +| `rating` | `{"max":5,"icon":"star","color":"yellowBright"}` | +| `number` | `{"formatting":{"precision":0,"type":"decimal"}}` | +| `date` | `{"formatting":{"date":"YYYY-MM-DD","time":"None","timeZone":""}}` | +| `button` | `{"label":"Button","color":"teal"}` | +| `singleSelect` / `multipleSelect` | Choices auto-colored if no `color` in choice objects | + +--- + +## Typecast Reference + +`--typecast` converts human-readable values to internal IDs on `record create`/`record update`. + +| Field type | Without typecast | With typecast | +|-----------|-----------------|---------------| +| `user` | `{"id":"usrXXX","title":"Alice"}` | `"Alice"` or `"alice@example.com"` | +| `link` (single) | `{"id":"recXXX"}` or `[{"id":"recXXX"}]` | `"Record Display Name"` | +| `link` (multi) | `[{"id":"recA"},{"id":"recB"}]` | `["Record A","Record B"]` | + +**When to use**: always use `--typecast` when creating/updating records with user or link fields — it eliminates manual ID lookups. + +--- + +## Field Type Selection Guide + +| Data shape | Use | Not | +|-----------|-----|-----| +| Fixed options (status, priority) | `singleSelect` | `singleLineText` (no color/filter) | +| Multiple tags per record | `multipleSelect` | `singleSelect` (only one value) | +| Numbers with decimals | `number` with `precision` | `singleLineText` (can't calculate) | +| 1–10 ratings | `rating` | `number` (no star UI) | +| Yes/no flags | `checkbox` | `number` 0/1 (worse UX) | +| Dates/times | `date` with time formatting | `singleLineText` (can't sort/filter) | +| Reference another table | `link` | `singleSelect` (hardcoded list; breaks when added) | +| Auto-increment IDs | `autoNumber` | Manual numbering | +| Trigger an action | `button` | Any other type | diff --git a/.agents/skills/inkreach-teable/api-reference/trigger-types.md b/.agents/skills/inkreach-teable/api-reference/trigger-types.md new file mode 100644 index 0000000..20d823f --- /dev/null +++ b/.agents/skills/inkreach-teable/api-reference/trigger-types.md @@ -0,0 +1,236 @@ +# Trigger Types Reference + +All 8 triggers of the inkreach-teable automation engine. **Use these exact config keys** — they differ from official teable and from the (stale) `docs/automation/triggers.md`. + +Every trigger node has this shape (stored in `workflow_node`): +```json +{ + "id": "node-1", + "type": "trigger", + "category": "", + "config": { ... }, + "name": null +} +``` + +> You don't build this JSON by hand — use `inkreach-teable trigger set --type ...` which constructs it correctly. + +--- + +## 1. recordCreated — When a record is created + +```bash +inkreach-teable trigger set --type recordCreated --table-id tblXXX \ + [--conditions ''] \ + [--batch '{"enabled":true,"batchWindow":2000,"batchSize":100}'] +``` + +**Config:** +```json +{ + "tableId": "tblXXX", + "conditions": null, + "batch": { "enabled": false, "batchWindow": 2000, "batchSize": 100 } +} +``` + +**Output variables** (available to downstream actions): +- `record.id`, `record.fields.` +- `user` (who created) +- `tableId` + +--- + +## 2. recordUpdated — When a record is updated + +```bash +inkreach-teable trigger set --type recordUpdated --table-id tblXXX \ + --field-ids '["fldXXX"]' \ + [--batch '{...}'] +``` + +**Config:** +```json +{ + "tableId": "tblXXX", + "fieldIds": ["fldXXX"], + "batch": { ... } +} +``` +- `fieldIds: []` (empty) = watch **all** fields (including future-created ones). +- ⚠️ **Key is `fieldIds`, NOT `watchFieldIds`.** +- ⚠️ recordUpdated **ignores `conditions`** — use `recordCondition` if you need filtering. + +**Output:** `record.id`, `record.fields.*`, `record.oldFields.*` (pre-update values), `user`, `tableId`, `changedFieldIds`. + +--- + +## 3. recordCondition — When a record matches conditions + +```bash +inkreach-teable trigger set --type recordCondition --table-id tblXXX \ + --conditions '{"conjunction":"and","filterSet":[{"fieldId":"fldStatus","operator":"is","value":"Done"}]}' +``` + +**Config:** +```json +{ + "tableId": "tblXXX", + "conditions": { "conjunction": "and", "filterSet": [ ... ] }, + "batch": { ... } +} +``` +- ⚠️ **Category is `recordCondition`, NOT `recordMatchesConditions`.** +- `conditions` is **required**. +- Fires on create AND update when the record transitions to matching. + +**conditions structure** (same as view filters): +```json +{ + "conjunction": "and" | "or", + "filterSet": [ + { "fieldId": "fldXXX", "operator": "is", "value": "Done" }, + { "fieldId": "fldNum", "operator": "isGreater", "value": 100 } + ] +} +``` +Common operators: `is`, `isNot`, `isEmpty`, `isNotEmpty`, `isAnyOf`, `isNoneOf`, `contains`, `isGreater`, `isLess`, `isAfter`, `isBefore`. + +**Output:** `record.id`, `record.fields.*`, `record.oldFields.*`, `user`, `tableId`. + +--- + +## 4. recordCreatedOrUpdated — When a record is created or updated + +```bash +inkreach-teable trigger set --type recordCreatedOrUpdated --table-id tblXXX \ + [--conditions ''] \ + [--batch '{...}'] +``` + +**Config:** +```json +{ "tableId": "tblXXX", "conditions": null, "batch": { ... } } +``` +- Ignores `fieldIds` (watches all fields). +- `conditions` optional. + +**Output:** same as recordCreated + `record.oldFields.*` on updates. + +--- + +## 5. formSubmitted — When a form is submitted + +```bash +inkreach-teable trigger set --type formSubmitted --table-id tblXXX --form-id viwXXX +``` + +**Config:** +```json +{ "tableId": "tblXXX", "formId": "viwXXX" } +``` +- `formId` is the **form view's id** (a view of type `form`). + +**Output:** `record.id`, `record.fields.*`, `user`, `tableId`, `formId`. + +--- + +## 6. scheduledTime — On a schedule + +```bash +inkreach-teable trigger set --type scheduledTime \ + --schedule '{"timing":{"type":"minutes","interval":5}}' +``` + +**Config (only these fields are honored):** +```json +{ "timing": { "type": "minutes", "interval": 5 } } +``` + +| `timing.type` | `interval` range | Meaning | +|---------------|------------------|---------| +| `minutes` | 1–60 | Every N minutes | +| `hours` | 1–24 | Every N hours | +| `days` | 1–N | Every N days | +| `months` | 1–N | Every N months (uses a fixed day) | + +⚠️ **Backend limitations (known gotchas):** +- `triggerTime`, `weekdays`, `daysOfMonth`, `starting`, `ending`, `tz` are **all IGNORED**. +- `weeks` type is hardcoded to every Sunday (ignores `weekdays`). +- `OneTime` type is unsupported → falls back to every 60s. +- The scheduler is in-process `setInterval`; **multi-instance deployments fire duplicates**. + +If you need precise scheduling, set `interval` to a friendly value. Do NOT pass the extra fields — they give false confidence. + +**Output:** none useful (no record context); typically used with a `getRecords` + `script` action chain. + +--- + +## 7. buttonClick — When a button field is clicked + +```bash +inkreach-teable trigger set --type buttonClick --table-id tblXXX --field-ids '["fldGo"]' +``` + +**Config:** +```json +{ "tableId": "tblXXX", "fieldIds": ["fldGo"] } +``` +- The field **must be a Button-type field** whose `options.workflowId` points to this workflow. +- Create it: `inkreach-teable field create --table-id tblXXX --name "Go" --type button --options '{"workflowId":"wflXXX","label":"Go"}'` + +**Triggering:** the click comes from `POST /api/table/{tableId}/record/{recordId}/{fieldId}/button-click` (the web UI calls this; CLI tests via `fire button-click`). + +**Output:** `record.id`, `record.fields.*`, `fieldId`, `user`, `runId`, `name`, `tableId`, `workflowId`. + +⚠️ The Button field's `options` captures a stale `isActive` snapshot at creation. After deactivating, the API validates the DB `isActive` instead — so a deactivated workflow returns 400 "not active" on click. Re-`activate` to fix. + +--- + +## 8. webhook — On external HTTP POST + +```bash +inkreach-teable trigger set --type webhook +``` + +**Config:** +```json +{ "authorization": { "type": "none" } } +``` + +⚠️ **`authorization` is UI-only — the backend does NOT validate it.** Anyone with the URL can trigger it. Don't rely on it for security. + +**Real webhook URL:** +``` +POST http:///api/webhook/{baseId}/{workflowId} +``` +- ⚠️ The route is `/api/webhook/{baseId}/{workflowId}` — **no** `/base/` or `/workflow/` segments (the frontend UI shows a different, WRONG URL). +- Test: `inkreach-teable fire webhook --body '{"any":"json"}'` + +**Output:** `headers` (request headers), `body` (parsed JSON body), `method`, `baseId`, `webhookId`. + +--- + +## Batch Processing + +For `recordCreated`/`recordUpdated`/`recordCondition`/`recordCreatedOrUpdated`, add `--batch`: + +```json +{ "enabled": true, "batchWindow": 2000, "batchSize": 100 } +``` + +- Events within `batchWindow` ms (up to `batchSize`) are merged into ONE run. +- The script receives `triggerInput.records` (array) instead of `triggerInput.record` (single). +- Use `Promise.allSettled` in scripts to handle the array concurrently. + +```bash +inkreach-teable trigger set wflXXX --type recordCreated --table-id tblXXX \ + --batch '{"enabled":true,"batchWindow":2000,"batchSize":100}' +inkreach-teable action add wflXXX --type script --code ' + const records = triggerInput.records || [triggerInput.record]; + const results = await Promise.allSettled(records.map(async (r) => { + // process each record + })); + return { processed: results.length }; +' +``` diff --git a/.agents/skills/inkreach-teable/guides/automation-guide.md b/.agents/skills/inkreach-teable/guides/automation-guide.md new file mode 100644 index 0000000..fdf172c --- /dev/null +++ b/.agents/skills/inkreach-teable/guides/automation-guide.md @@ -0,0 +1,191 @@ +# Automation Guide + +Automations (workflows) are event-driven: a **trigger** fires, then **actions** run in topological order. inkreach-teable's automation engine is self-built — use the `inkreach-teable` CLI (not official `teable`). + +> **Required reading before configuring triggers**: [trigger-types.md](../api-reference/trigger-types.md) +> **Required reading before writing script actions**: [action-types.md](../api-reference/action-types.md) + +## Mental Model + +``` +[Trigger] → [Action 1] → [Action 2] → ... → [triggerEnd] +``` + +- A workflow has **exactly one trigger node** (type `trigger`) and N action nodes (type `action`). +- Nodes connect via **edges** (linear chain; each node has at most one upstream — no parallel branches). +- **Activating** freezes a snapshot; execution runs against the snapshot, not the draft. +- `hasDraft: true` = unactivated changes exist; re-`activate` to apply. + +## Available Commands + +| Command | Purpose | +|---------|---------| +| `workflow list` | List all workflows in the base | +| `workflow get ` | Detail (nodes + edges + config) | +| `workflow create --name ` | Create (auto-adds a recordCreated trigger) | +| `workflow update [--name] [--description]` | Rename | +| `workflow delete ` | Soft-delete | +| `workflow activate ` | Snapshot + register trigger + create auto-token | +| `workflow deactivate ` | Unregister trigger | +| `workflow test ` | Mock-input test run (no trigger, no queue) | +| `trigger set --type ...` | Set trigger type + config | +| `action add --type ...` | Add an action node | +| `action update ...` | Update an action node | +| `action delete ` | Remove an action node | +| `secret set/list/delete` | Manage script secrets | +| `runs list/get/rerun` | Execution history | +| `fire button-click/webhook` | Manually trigger for testing | + +## Trigger Selection + +| User scenario | `--type` | Key options | +|--------------|----------|-------------| +| React to new records | `recordCreated` | `--table-id` | +| React to record changes | `recordUpdated` | `--table-id`, `--field-ids` (empty = all) | +| React when a condition matches | `recordCondition` | `--table-id`, `--conditions` (required) | +| React to create OR update | `recordCreatedOrUpdated` | `--table-id`, `--conditions` (optional) | +| Form submission | `formSubmitted` | `--table-id`, `--form-id` | +| Schedule (only type+interval work) | `scheduledTime` | `--schedule '{"timing":{"type":"minutes","interval":5}}'` | +| Button field click | `buttonClick` | `--table-id`, `--field-ids` | +| External HTTP POST | `webhook` | (none; auth backend-ignored) | + +**Batch processing** (record triggers only): add `--batch '{"enabled":true,"batchWindow":2000,"batchSize":100}'` to merge events within a window into a single run. The script receives an array of records. + +Full config details: [trigger-types.md](../api-reference/trigger-types.md). + +## Action Selection + +| Goal | `--type` | Key options | +|------|----------|-------------| +| Run JS code (transform, call APIs, compute) | `script` | `--code ''` or `--code-file `, `--timeout ` | +| Send email | `sendEmail` | `--to`, `--subject`, `--body ` | +| Call external HTTP | `httpRequest` | `--url`, `--method`, `--headers `, `--req-body ` | +| Create records | `createRecord` | `--target-table `, `--records ` | +| Update records | `updateRecord` | `--target-table `, `--records ` | +| Query records | `getRecords` | `--target-table `, `--query ` | + +Full config details: [action-types.md](../api-reference/action-types.md). + +## Creation Workflow + +```bash +# 1. Gather IDs +inkreach-teable table get tblXXX # confirm table +inkreach-teable field list tblXXX # find field ids for triggers + +# 2. Create workflow +inkreach-teable workflow create --name "My automation" +# → returns wflXXX + +# 3. Set trigger +inkreach-teable trigger set wflXXX --type recordUpdated \ + --table-id tblXXX --field-ids '["fldStatus"]' + +# 4. Add action(s) +inkreach-teable action add wflXXX --type script \ + --code 'return { status: triggerInput.record.fields.Status }' + +# 5. (Optional) add secrets the script needs +inkreach-teable secret set wflXXX --name API_KEY --value sk_xxx + +# 6. Activate +inkreach-teable workflow activate wflXXX +# → returns snapshotId + +# 7. Test +inkreach-teable fire button-click tblXXX recXXX fldXXX # for buttonClick +# or just perform the triggering action (create/update a record, POST webhook...) +inkreach-teable runs list wflXXX +``` + +## Editing an Existing Workflow + +```bash +# Change trigger config +inkreach-teable trigger set wflXXX --type buttonClick --table-id tblXXX --field-ids '["fldNew"]' +# Add another action +inkreach-teable action add wflXXX --type sendEmail --to a@b.com --subject Done --body '

ok

' +# Remove an action +inkreach-teable action delete wflXXX node-12345 +# RE-ACTIVATE to snapshot the changes +inkreach-teable workflow activate wflXXX +``` + +`trigger set` / `action add/update/delete` internally do GET→modify→PUT the whole graph, so they're safe. But you **must re-activate** for changes to take effect at runtime. + +## Secrets + +Scripts often need API keys. Never hardcode — use secrets. + +```bash +inkreach-teable secret set wflXXX --name OPENAI_KEY --value sk-xxx --description "for AI calls" +inkreach-teable secret list wflXXX # names only — value never returned +inkreach-teable secret update wflXXX secXXX --value sk-new +inkreach-teable secret delete wflXXX secXXX +``` + +In a script action, read via `process.env.OPENAI_KEY`. Secrets are AES-encrypted at rest. + +## Testing / Triggering + +Most triggers fire from real events (record create/update, form submit, schedule). Two triggers are manually fireable for testing: + +```bash +# Button field click +inkreach-teable fire button-click + +# Webhook (no auth needed) +inkreach-teable fire webhook --body '{"event":"test"}' +``` + +For other triggers, test by performing the real action (create/update a record, submit the form). Then inspect: + +```bash +inkreach-teable runs list wflXXX --status failed +inkreach-teable runs get wflXXX +# rerun a failed execution +inkreach-teable runs rerun wflXXX +``` + +## Variable References (cross-node data) + +An action can reference upstream nodes' output in string fields via `{{nodeIndex.path}}`: + +- `{{1.record.id}}` — trigger record's id (trigger = node 1) +- `{{1.record.fields.Status}}` — trigger record's Status field +- `{{2.records}}` — output of the first action (node 2) + +Node index follows **topological execution order** (trigger is always 1). Script actions can also read the merged upstream output directly via `triggerInput`. + +## Common Patterns + +### Notify on status change +```bash +inkreach-teable workflow create --name "Status→email" +inkreach-teable trigger set wflXXX --type recordUpdated --table-id tblXXX --field-ids '["fldStatus"]' +inkreach-teable action add wflXXX --type sendEmail \ + --to ops@x.com --subject 'Status changed' \ + --body '{{1.record.fields.Status}}' +inkreach-teable workflow activate wflXXX +``` + +### Daily digest +```bash +inkreach-teable trigger set wflXXX --type scheduledTime \ + --schedule '{"timing":{"type":"hours","interval":24}}' +``` + +### Button → script → update another table +```bash +inkreach-teable trigger set wflXXX --type buttonClick --table-id tblA --field-ids '["fldGo"]' +inkreach-teable action add wflXXX --type script --code ' + const r = triggerInput.record; + // call back into the instance API using the auto-injected token + const res = await fetch(triggerInput._apiBaseUrl + "/api/table/tblB/record", { + method: "POST", + headers: { Authorization: "Bearer " + triggerInput._accessToken, "Content-Type": "application/json" }, + body: JSON.stringify({ fields: { Source: r.id } }) + }); + return { created: await res.json() }; +' +``` diff --git a/.agents/skills/inkreach-teable/guides/cli-reference.md b/.agents/skills/inkreach-teable/guides/cli-reference.md new file mode 100644 index 0000000..bdd79a4 --- /dev/null +++ b/.agents/skills/inkreach-teable/guides/cli-reference.md @@ -0,0 +1,182 @@ +# CLI Reference + +Complete command reference for `inkreach-teable`. Global options apply to all commands. + +## Global Options + +| Option | Description | +|--------|-------------| +| `--endpoint ` | Backend URL (default `http://localhost:3000`) | +| `--token ` | Personal Access Token | +| `--base-id ` | Default base id | +| `--json` | Output raw JSON (for agent parsing) | +| `--debug` | Print HTTP requests/responses to stderr | +| `-v, --version` | Show version | + +Config precedence: global flag > env (`INKREACH_TEABLE_ENDPOINT`/`INKREACH_TEABLE_TOKEN`/`INKREACH_TEABLE_BASE_ID`) > `~/.inkreach-teable/config.json`. + +## auth + +```bash +inkreach-teable auth status # verify token + show user +inkreach-teable auth login --endpoint --token [--base-id ] +inkreach-teable auth token list +inkreach-teable auth token create --name [--scopes ] [--base-ids ] [--full-access] [--expires ] +inkreach-teable auth token delete +``` + +- `auth token create` returns the full token **only once**. Default scopes: `automation|read,automation|create,automation|update,automation|delete,table|read,record|read,record|create,record|update`. + +## base / table / field / record / view + +These resources use the official-teable-compatible API. Full guide: [tables-fields.md](tables-fields.md). + +```bash +# base +inkreach-teable base list +inkreach-teable base get + +# table +inkreach-teable table list [--base-id ] +inkreach-teable table get +inkreach-teable table create --name [--base-id ] [--fields ''] +inkreach-teable table update --name +inkreach-teable table delete + +# field +inkreach-teable field list +inkreach-teable field get +inkreach-teable field create --table-id --name --type [--options ''] +inkreach-teable field update [--name ] [--options ''] +inkreach-teable field delete + +# record +inkreach-teable record get --table-id [--filter ''] [--projection ''] [--page-size ] [--page ] [--search ] +inkreach-teable record get-one +inkreach-teable record create --table-id --fields '' # single +inkreach-teable record create --table-id --records '' # batch +inkreach-teable record update --table-id --records '' +inkreach-teable record delete --table-id --record-ids + +# view +inkreach-teable view list +inkreach-teable view get +inkreach-teable view create --table-id --name [--type ] +inkreach-teable view update [--name ] +inkreach-teable view delete +``` + +## workflow + +```bash +inkreach-teable workflow list [--base-id ] +inkreach-teable workflow get [--base-id ] +inkreach-teable workflow create --name [--description ] [--base-id ] +inkreach-teable workflow update [--name ] [--description ] [--base-id ] +inkreach-teable workflow delete [--base-id ] +inkreach-teable workflow activate [--base-id ] +inkreach-teable workflow deactivate [--base-id ] +inkreach-teable workflow test [--base-id ] +``` + +Full automation flow: [automation-guide.md](automation-guide.md). + +## trigger + +```bash +inkreach-teable trigger set --type [options] [--base-id ] +``` + +| `--type` | Required options | +|----------|------------------| +| `recordCreated` | `--table-id` | +| `recordUpdated` | `--table-id`, `--field-ids ''` | +| `recordCondition` | `--table-id`, `--conditions ''` | +| `recordCreatedOrUpdated` | `--table-id` | +| `formSubmitted` | `--table-id`, `--form-id ` | +| `scheduledTime` | `--schedule '{"timing":{"type":"minutes","interval":5}}'` | +| `buttonClick` | `--table-id`, `--field-ids ''` | +| `webhook` | (none) | + +Common optional: `--batch ''` (record triggers only). + +Details: [trigger-types.md](../api-reference/trigger-types.md). + +## action + +```bash +inkreach-teable action add --type [options] [--base-id ] +inkreach-teable action update [--config ''] [--code ] [--code-file ] [--name ] +inkreach-teable action delete [--base-id ] +``` + +Per-type options: + +| `--type` | Options | +|----------|---------| +| `script` | `--code ` / `--code-file `, `--timeout ` | +| `sendEmail` | `--to `, `--subject `, `--body ` | +| `httpRequest` | `--url `, `--method `, `--headers `, `--req-body ` | +| `createRecord` | `--target-table `, `--records ` | +| `updateRecord` | `--target-table `, `--records ` | +| `getRecords` | `--target-table `, `--query ` | + +Details: [action-types.md](../api-reference/action-types.md). + +## secret + +```bash +inkreach-teable secret list [--base-id ] +inkreach-teable secret set --name --value [--description ] [--base-id ] +inkreach-teable secret update [--name ] [--value ] [--description ] [--base-id ] +inkreach-teable secret delete [--base-id ] +``` + +Values are write-only — `list` never returns them. Scripts read via `process.env.`. + +## runs + +```bash +inkreach-teable runs list [--status failed|succeeded|running] [--base-id ] +inkreach-teable runs get [--base-id ] +inkreach-teable runs rerun [--base-id ] +``` + +`rerun` only works on `failed` executions (backend validates). + +## fire + +```bash +inkreach-teable fire button-click +inkreach-teable fire webhook [--body ''] [--method ] +``` + +Manually trigger for testing. Webhook needs no auth (backend is `@Public`). + +## sql + +```bash +inkreach-teable sql '