fix(cli): 轨迹分析驱动的 8 处命令修复 + 技能文档同步 (0.1.5)
- table get/update: 改用真实路由 /api/base/:baseId/table/:tableId(/name)
- view get/update/delete: table 域路由;view update 实现文档承诺的
--filter/--sort/--group/--column-meta(sort 自动包装 sortObjs)
- record create --fields 自动包装 {records:[{fields}]}
- record delete 单条走单删端点(修复单值 query 解析成 string 的 400),
支持位置参数;record list 加为 record get 别名
- record get-one / record get --record-id 走 table 域路由
- 移除 sql 命令:后端无 SQL 查询 API(404),技能文档 5 处同步改写
- plugin 技能 dev-workflow 内联 known-good webpack.config
(禁止 output.library.type=iife,评估轨迹中 with_skill 组也反复踩坑)
This commit is contained in:
@@ -128,6 +128,40 @@ pnpm build
|
||||
# or: npm run build
|
||||
```
|
||||
|
||||
**Rebuilding without the scaffold — use this exact config** (agents hand-writing
|
||||
webpack configs keep hitting `Error: Unsupported library type iife` — there is
|
||||
NO `output.library` in a working plugin config; a default single-entry
|
||||
production build is already IIFE, which is what the upload validator requires):
|
||||
|
||||
```js
|
||||
// webpack.config.js — known-good (from plugins/batch-edit-pro/)
|
||||
const path = require('path');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
|
||||
module.exports = {
|
||||
mode: 'production', // default single-entry build emits an IIFE (!function / (()=>))
|
||||
entry: './src/index.tsx',
|
||||
output: { path: path.resolve(__dirname, 'dist'), filename: 'main.js', clean: true },
|
||||
// NO output.library — never add library.type: 'iife' (webpack rejects it)
|
||||
externals: {
|
||||
'react': 'window.__TEABLE_HOST__.React',
|
||||
'react-dom': 'window.__TEABLE_HOST__.ReactDOM',
|
||||
},
|
||||
resolve: { extensions: ['.tsx', '.ts', '.js', '.jsx'] },
|
||||
module: {
|
||||
rules: [
|
||||
{ test: /\.tsx?$/, loader: 'ts-loader', options: { transpileOnly: true }, exclude: /node_modules/ },
|
||||
{ test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader'] },
|
||||
],
|
||||
},
|
||||
plugins: [new MiniCssExtractPlugin({ filename: 'styles.css' })],
|
||||
};
|
||||
```
|
||||
|
||||
If you build outside the monorepo, drop the `resolve.alias` block (it exists
|
||||
only so TS can type-check `import type` from `@teable/*` sources) and use
|
||||
`import type` + `as unknown as` casts instead of runtime imports.
|
||||
|
||||
Outputs: `dist/main.js` (IIFE) + `dist/styles.css` (if styles exist).
|
||||
|
||||
Verify the output:
|
||||
|
||||
@@ -25,14 +25,13 @@ Respond in the user's language. Keep answers concise and action-oriented.
|
||||
| Module | What it solves | Entry commands | Detail doc |
|
||||
|--------|---------------|----------------|------------|
|
||||
| Tables & Fields | Create/modify structure; **inspect select-choice constraints** | `table create`, `field create`, `field list --with-options`, `field get` | [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` | [tables-fields.md § Records](guides/tables-fields.md) |
|
||||
| Records | Read/write row data | `record get` (alias `record list`), `record create/update/delete` | [tables-fields.md § Records](guides/tables-fields.md) |
|
||||
| Views | Filtered/sorted perspectives | `view create/update` | [tables-fields.md § Views](guides/tables-fields.md) |
|
||||
| View presets | View-scoped shared filter/sort bundles + per-user personal config, applied via toolbar | `preset create/update/list/delete` | [tables-fields.md § View Presets](guides/tables-fields.md) |
|
||||
| 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 | API keys for script actions | `secret set/list/delete` | [automation-guide.md § Secrets](guides/automation-guide.md) |
|
||||
| Execution History | Run results / rerun failures | `runs list/get/rerun` | [automation-guide.md § Testing](guides/automation-guide.md) |
|
||||
| Manual Trigger | Test button-click/webhook | `fire button-click`, `fire webhook` | [automation-guide.md § Testing](guides/automation-guide.md) |
|
||||
| SQL | Read-only analytics, JOINs | `sql '<sql>'` | [cli-reference.md § sql](guides/cli-reference.md) |
|
||||
| Direct REST API | Integrate external scripts/services; endpoints the CLI lacks; script actions calling back | `curl` / `fetch` + `Authorization: Bearer <PAT>` | [rest-api.md](api-reference/rest-api.md) |
|
||||
| Plugins | Dynamic plugins, dev whitelist, CSP | `plugin *` | [cli-reference.md § plugin](guides/cli-reference.md) |
|
||||
| Subscriptions | Record digest/instant email | `subscription *` | [subscription.md](guides/subscription.md) |
|
||||
@@ -54,8 +53,8 @@ Full command syntax: [cli-reference.md](guides/cli-reference.md). Instance acces
|
||||
| Send notification | `sendNotification` (in-app) | `sendEmail` (needs SMTP) unless email required |
|
||||
| **Inspect select-choice constraints** | `field list --with-options`, `field get <tbl> <name>`, `field visible-choices` | `--json` + manual filtering |
|
||||
| **Write a select-field value** | Verify the value exists in choices first (`field list --with-options`); need a new value → `field update --options` to add the choice, then write | Blind `record update` (400, or typecast silently resolves to null / mutationApplied=false — it never creates choices) |
|
||||
| Read records for later writes | `record get` (returns record IDs) | `sql` (no record IDs) |
|
||||
| Analytics / JOIN | `sql` with GROUP BY | Fetch all records + compute |
|
||||
| Read records for later writes | `record get` / `record list` (returns record IDs) | Official-teable SQL APIs (none here) |
|
||||
| Analytics / aggregation | `record get --filter` + compute, script action, or grouped grid view | `sql` — this deployment has NO SQL query API (CLI 0.1.5 removed the dead command) |
|
||||
| Store an API key for scripts | `secret set` → `process.env.NAME` | Hardcoding |
|
||||
| Workflow didn't run | `runs list --status failed` → `runs get` | Blind re-activate |
|
||||
| Call the instance from external code / a route the CLI lacks | Direct REST: `Authorization: Bearer <PAT>` + routes in [rest-api.md](api-reference/rest-api.md); unknown route → run the nearest CLI command with `--debug` and copy its request | Official teable API docs (different automation contract) |
|
||||
@@ -76,12 +75,16 @@ These differ from official teable and from the (stale) `docs/automation/triggers
|
||||
10. **Button field binding**: `options.workflow` is an **object** `{id, name?}`, not a bare id string. The field's `isActive` snapshot can go stale; the API validates the workflow's DB `isActive` at click time (deactivated → 400).
|
||||
11. **Script sandbox** has NO `fs`/`eval`/`Function`, but has `triggerInput`, `console`, `fetch`, `process.env` (secrets + `AUTOMATION_TOKEN` + `PUBLIC_ORIGIN`), and a controlled `require`. Call back into the instance via `triggerInput._accessToken` + `triggerInput._apiBaseUrl`. **npm deps**: declare with `action add/update --dependencies '[{"name":"axios","version":"1.6.0"}]'` (or `axios@1.6.0,dayjs`). Requires `npm` on the backend **service** PATH — systemd user units default to a PATH that omits the nvm bin dir, so deps fail with `spawn npm ENOENT`; fix by adding `Environment=PATH=<nvm-bin>:/usr/local/sbin:...` to `teable-backend.service`.
|
||||
12. **Field options updates go through the convert API** — `field update --options` handles this; the plain PATCH update API strips `options`.
|
||||
13. **No SQL query API exists** — the backend has no SQL endpoint; the CLI `sql` command was removed in 0.1.5. Aggregate via `record get --filter` + a script action, or a grouped grid view. Also: `table get/update` are base-scoped (`--base-id`, defaults to the configured base) and `view get/update/delete` + `record get-one` are table-scoped (`--table-id`).
|
||||
|
||||
## 4. Quick Syntax
|
||||
|
||||
```bash
|
||||
inkreach-teable auth login --endpoint https://table.inkreach.cc --token <PAT> # once
|
||||
inkreach-teable table create --name "Tasks" --fields '[{"name":"Title","type":"text"},{"name":"Status","type":"singleSelect","options":{"choices":["Todo","Done"]}}]'
|
||||
inkreach-teable record create --table-id tblXXX --fields '{"Title":"a"}' # 单条;批量用 --records '[{"fields":{...}}]'
|
||||
inkreach-teable record list --table-id tblXXX [--filter '<json>'] # 查询/分页(= record get)
|
||||
inkreach-teable record delete --table-id tblXXX recXXX [recYYY ...] # 也可 --record-ids 'recA,recB'
|
||||
inkreach-teable workflow create --name "On Go click" # → wflXXX
|
||||
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'
|
||||
@@ -103,4 +106,6 @@ inkreach-teable fire button-click tblXXX recXXX fldXXX && inkreach-teable runs l
|
||||
| `runs list` shows failed | Action error | `runs get <wfId> <execId>` → `nodeResults[].error` |
|
||||
| Script deps fail `spawn npm ENOENT` | `npm` not on service PATH | See Key Constraint 11 |
|
||||
| Record write 400 "does not meet the current row conditions" | Value violates choice `visibilityFilter` | `field visible-choices` to see allowed options |
|
||||
| `sql: unknown command` / 404 on a SQL endpoint | No SQL query API on this deployment | Aggregate via script action or `record get --filter` |
|
||||
| `record list` works like `record get` | Intentional alias (CLI ≥0.1.5) | — |
|
||||
| Select-field write "succeeds" but value unchanged (or becomes null) | Value not in choices — without typecast the API 400s; with typecast it resolves to null and no-ops (`mutationApplied=false`), never creates the choice | `field list --with-options` to confirm; `field update --options` to add the choice, then write |
|
||||
|
||||
@@ -43,7 +43,7 @@ Verified route-for-route against `src/lib/client.ts` (the CLI's HTTP layer).
|
||||
List-query params (objects are passed **JSON-stringified**; repeated keys use
|
||||
`key[]`): `filter` (JSON), `projection` (JSON array), `search[]` (keyword —
|
||||
MUST be the `search[]` tuple form, plain `search=` is ignored), `skip` / `take`
|
||||
(pagination; no `orderBy` on this fork — sort via view or `sql`). Response:
|
||||
(pagination; no `orderBy` on this fork — sort via a view). Response:
|
||||
`{ records: [{id, fields}], total? }`.
|
||||
|
||||
### Fields
|
||||
@@ -83,7 +83,6 @@ MUST be the `search[]` tuple form, plain `search=` is ignored), `skip` / `take`
|
||||
|
||||
| Method & route | Purpose |
|
||||
|---|---|
|
||||
| `POST /api/base/{bid}/sql` | Read-only SQL (`{sql}`; JOIN/GROUP BY; no record ids) |
|
||||
| `GET /api/table/{tid}/aggregation/row-count` | Total row count (`{"rowCount":N}` — verified) |
|
||||
| `GET /api/table/{tid}/aggregation…` | Stats family (group-points, calendar-daily, search-count…) — see swagger for params |
|
||||
| `GET`/`POST` `/api/access-token`, `DELETE /api/access-token/{id}` | PAT management |
|
||||
|
||||
@@ -56,12 +56,13 @@ inkreach-teable field visible-choices <tableId> <recordId> <fieldIdOrName>
|
||||
inkreach-teable field delete <tableId> <fieldId>
|
||||
|
||||
# record
|
||||
inkreach-teable record get --table-id <tid> [--filter '<json>'] [--projection '<json>'] [--page-size <n>] [--page <n>] [--search <kw>]
|
||||
inkreach-teable record get-one <recordId>
|
||||
inkreach-teable record get --table-id <tid> [--filter '<json>'] [--projection '<json>'] [--page-size <n>] [--page <n>] [--search <kw>] [--record-id <rid>]
|
||||
inkreach-teable record list --table-id <tid> [...] # alias of record get
|
||||
inkreach-teable record get-one <recordId> --table-id <tid>
|
||||
inkreach-teable record create --table-id <tid> --fields '<json>' # single
|
||||
inkreach-teable record create --table-id <tid> --records '<json>' # batch
|
||||
inkreach-teable record update --table-id <tid> --records '<json>'
|
||||
inkreach-teable record delete --table-id <tid> --record-ids <csv>
|
||||
inkreach-teable record delete --table-id <tid> <recordId> [recordId ...] # or --record-ids <csv>
|
||||
|
||||
# view
|
||||
inkreach-teable view list <tableId>
|
||||
@@ -181,18 +182,6 @@ inkreach-teable subscription send <subscriptionId>
|
||||
Record digest/instant email subscriptions. Options (`--filter`, `--send-mode`,
|
||||
`--schedule-*`, `--trigger-on-enter`, …) and semantics: [subscription.md](subscription.md).
|
||||
|
||||
## sql
|
||||
|
||||
```bash
|
||||
inkreach-teable sql '<SELECT ...>' [--base-id <id>]
|
||||
```
|
||||
|
||||
Read-only. Must use `dbTableName`/`dbFieldName` (from `table get`/`field get`), double-quote identifiers, add `LIMIT 100`.
|
||||
|
||||
```bash
|
||||
inkreach-teable sql 'SELECT "name" FROM "bseXXX"."tbl_abc" LIMIT 10'
|
||||
```
|
||||
|
||||
## plugin
|
||||
|
||||
Manage plugins and developer whitelist.
|
||||
|
||||
@@ -57,7 +57,7 @@ With select options: `"Status:sel:Todo,Doing,Done"`, `"Tags:mul:Bug,Feature,UI"`
|
||||
### Update & Delete
|
||||
|
||||
```bash
|
||||
inkreach-teable table update <tableId> --name "<newName>" [--description <d>]
|
||||
inkreach-teable table update <tableId> --name "<newName>" [--base-id <id>]
|
||||
inkreach-teable table delete <tableId>
|
||||
```
|
||||
|
||||
@@ -70,10 +70,11 @@ inkreach-teable table delete <tableId>
|
||||
|
||||
```bash
|
||||
inkreach-teable table list [--base-id <id>]
|
||||
inkreach-teable table get <tableId>
|
||||
inkreach-teable table get <tableId> [--base-id <id>]
|
||||
```
|
||||
|
||||
`table get` returns `dbTableName` — the internal SQL table name used in `sql` queries (e.g., `SELECT * FROM "bseXXX"."tbl_abc"`).
|
||||
`table get` returns `dbTableName` — the internal PG table name (useful for
|
||||
maintainer-level psql inspection; there is NO SQL query API on this deployment).
|
||||
|
||||
---
|
||||
|
||||
@@ -162,8 +163,8 @@ Single/multiple-select choices may carry a `visibilityFilter` making them visibl
|
||||
inkreach-teable record get --table-id <tid> [--filter '<json>'] [--projection '<json>'] \
|
||||
[--page-size <n>] [--page <n>] [--search <kw>]
|
||||
|
||||
# Single record by ID
|
||||
inkreach-teable record get-one <recordId>
|
||||
# Single record by ID (table-scoped route)
|
||||
inkreach-teable record get-one <recordId> --table-id <tid>
|
||||
```
|
||||
|
||||
| Flag | Description |
|
||||
@@ -271,7 +272,7 @@ inkreach-teable record update --table-id tblXXX \
|
||||
### Delete
|
||||
|
||||
```bash
|
||||
inkreach-teable record delete --table-id <tid> --record-ids "recA,recB,recC"
|
||||
inkreach-teable record delete --table-id <tid> recA recB recC # or --record-ids "recA,recB,recC"
|
||||
```
|
||||
|
||||
Max 1000 IDs per call. Irreversible.
|
||||
@@ -284,7 +285,7 @@ Max 1000 IDs per call. Irreversible.
|
||||
|
||||
```bash
|
||||
inkreach-teable view list <tableId>
|
||||
inkreach-teable view get <viewId>
|
||||
inkreach-teable view get <viewId> --table-id <tid>
|
||||
```
|
||||
|
||||
### Create
|
||||
@@ -304,8 +305,8 @@ inkreach-teable view create --table-id <tid> --name "<name>" [--type <t>]
|
||||
### Update (configure filters, sorts, grouping)
|
||||
|
||||
```bash
|
||||
inkreach-teable view update <viewId> \
|
||||
[--name "<n>"] \
|
||||
inkreach-teable view update <viewId> --table-id <tid> \
|
||||
[--name "<n>"] [--description "<d>"] \
|
||||
[--filter '<json>'] \
|
||||
[--sort '<json>'] \
|
||||
[--group '<json>'] \
|
||||
@@ -314,7 +315,7 @@ inkreach-teable view update <viewId> \
|
||||
|
||||
**Filters**: `{"conjunction":"and","filterSet":[{"fieldId":"fldXXX","operator":"is","value":"Done"}]}`
|
||||
|
||||
**Sorts**: `[{"fieldId":"fldXXX","order":"asc"}]`
|
||||
**Sorts**: `[{"fieldId":"fldXXX","order":"asc"}]` (the CLI wraps it into `{sortObjs:[...]}` for the API)
|
||||
|
||||
**Group**: `[{"fieldId":"fldXXX","order":"asc"}]`
|
||||
|
||||
@@ -323,7 +324,7 @@ inkreach-teable view update <viewId> \
|
||||
### Delete
|
||||
|
||||
```bash
|
||||
inkreach-teable view delete <viewId>
|
||||
inkreach-teable view delete <viewId> --table-id <tid>
|
||||
```
|
||||
|
||||
Deleting a view does NOT delete the table or its data — only the view configuration.
|
||||
|
||||
Reference in New Issue
Block a user