diff --git a/README.md b/README.md index 6e340df..8fe307e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,27 @@ # inkreach-teable-cli -把下面这段引导词发给任意机器 / 任意会话中的 Agent,即可完成安装并开始使用: +inkreach-teable 实例的运维 CLI(npm 包 `@inkreach/inkreach-teable`)与 +**全部 Agent Skills 的唯一维护仓库**。 + +## Skills(本仓库 `skills/` 目录 = single source of truth) + +| Skill | 用途 | +|---|---| +| `inkreach-teable` | 操作已部署实例:表格/字段/记录/视图/自动化日常运维、REST API 参考 | +| `inkreach-teable-plugin` | 开发动态 React 组件插件:IIFE bundle(webpack/vite 双模板)、多资产上传(chunk + sourcemap)、`teable-plugin dev` 热更新闭环、5 个挂载位(含整页 route) | + +**维护规则**:所有 skill 只在本仓库修改;主仓库(inkreach-teable)不含任何 +skill 源文件。改完后同步到安装位置(`~/.zcode/skills/` 为主, +`~/.agents/skills/` 为镜像): + +```bash +cp -r skills//SKILL.md skills//metadata.json skills//dynamic-plugins ~/.zcode/skills// +cp -r skills// ~/.agents/skills// # 按需镜像 +``` + +## 安装(任意机器 / 任意会话的 Agent) + +把下面这段引导词发给 Agent 即可完成安装并开始使用: ```text 请安装并用 inkreach-teable CLI 操作 inkreach-teable: @@ -11,3 +32,9 @@ 3. 鉴权(PAT 从 Web UI Settings → Personal Access Tokens 获取): inkreach-teable auth login --endpoint https://table.inkreach.cc --token --base-id bseXXX ``` + +## 目录结构 + +- `skills/` — Agent Skills(本仓库的核心资产之一) +- `src/` — CLI 源码(commander 实现) +- `docs/cli-skill-design.md` — CLI + Skill 的设计文档 diff --git a/skills/inkreach-teable-plugin/SKILL.md b/skills/inkreach-teable-plugin/SKILL.md index d5641bf..eb97d32 100644 --- a/skills/inkreach-teable-plugin/SKILL.md +++ b/skills/inkreach-teable-plugin/SKILL.md @@ -23,18 +23,31 @@ rebuilding the host Next.js app. Plugins are IIFE bundles — they ship as plain JS that executes in the host page and accesses host APIs through `window.__TEABLE_HOST__`. -> **Contract**: A plugin = a webpack IIFE bundle (`dist/main.js` + optional -> `dist/styles.css`) + `plugin.json` manifest, uploaded to the host via -> `POST /api/plugin/:id/upload-bundle`. It runs in the same React tree as the host -> and has full access to the host's SDK, UI library, and OpenAPI helpers. +> **Contract**: A plugin = a webpack (or vite) IIFE bundle (`dist/main.js` + +> optional lazy chunks `*.chunk.js`, source maps `*.js.map`, `styles.css`) + +> a `plugin.json` manifest, uploaded to the host via +> `POST /api/plugin/:id/upload-bundle` (or `teable-plugin deploy`, which +> handles all assets). It runs in the same React tree as the host and has +> full access to the host's SDK, UI library, and OpenAPI helpers. +> +> **Dev loop**: `teable-plugin dev` (or `pnpm dev` in scaffolded projects) +> watch-builds and auto-uploads with a `devMode` manifest; the open host page +> remounts the plugin within ~4s without a page refresh. See +> `dynamic-plugins/dev-workflow.md`. +> +> **Full-page apps**: install at position `route` and the host serves the +> plugin on `/base//p/` (whole viewport, own router OK — +> HashRouter recommended). > > **Critical constraint**: no RUNTIME imports from `@teable/sdk`, `@teable/ui-lib`, -> `@teable/openapi` — webpack maps them to `window.__TEABLE_HOST__.*` externals, so a -> value import compiles to `undefined` at runtime. `import type { X } from '@teable/...'` -> IS fine (erased at compile time). `react`/`react-dom` may be imported ONLY when your -> webpack `externals` maps them to `window.__TEABLE_HOST__.React/ReactDOM` (see the -> batch-edit-pro webpack config). For everything else, read from the host global: -> `const { useFields } = window.__TEABLE_HOST__.SDK`. See +> `@teable/openapi` — a value import via monorepo aliases inlines the whole +> package source (via window-global externals it compiles to `undefined`). +> `import type { X } from '@teable/...'` IS fine (erased at compile time). +> `react`/`react-dom` may be imported when your webpack `externals` maps them +> to `window.__TEABLE_HOST__.React/ReactDOM` (see the plugin-showcase webpack +> config; `react/jsx-runtime` is intentionally bundled — element factories +> only, hooks stay on the host singleton). For everything else, read from the +> host global: `const { useFields } = window.__TEABLE_HOST__.SDK`. See > `dynamic-plugins/troubleshooting.md` Pitfall 1 for details and symptoms. ## 0. Two efficiency rules (read first) @@ -71,8 +84,9 @@ JS that executes in the host page and accesses host APIs through | 3. SDK API | `dynamic-plugins/sdk-api.md` | when writing plugin logic — definePlugin, PluginContext, events, storage, useSelection, native form embedding (`NativeForm` Tier-1 injection / `NativeFormScope`+`useNativeForm` Tier-2 kit), NativeGrid | | 3b. Native form quick answer | `dynamic-plugins/native-form.md` | **when the user ASKS whether/how to embed native field editors, custom form layout, or multi-step forms — read this FIRST and answer directly (consultative questions need no scaffold/build)** | | 4. Deployment | `dynamic-plugins/deployment.md` | when uploading + enabling + authority matrix permissions | -| 5. Troubleshooting | `dynamic-plugins/troubleshooting.md` | before writing logic — common pitfalls (host globals, developer whitelist, AM, empty states) | -| Ref: batch-edit-pro | `plugins/batch-edit-pro/` | the canonical example — contextMenu plugin, full React + SDK | +| 5. Troubleshooting | `dynamic-plugins/troubleshooting.md` | before writing logic — common pitfalls (host globals, developer whitelist, AM, empty states, chunk-name flattening, terser banners) | +| Ref: plugin-showcase | `plugins/plugin-showcase/` | the v2 example — lazy chunks + source maps + Tailwind + HashRouter route app | +| Ref: batch-edit-pro | `plugins/batch-edit-pro/` | the classic example — single-file contextMenu plugin, full React + SDK | ## 3. Quick decision: which position? diff --git a/skills/inkreach-teable-plugin/dynamic-plugins/deployment.md b/skills/inkreach-teable-plugin/dynamic-plugins/deployment.md index 27eaa04..6cbe1ab 100644 --- a/skills/inkreach-teable-plugin/dynamic-plugins/deployment.md +++ b/skills/inkreach-teable-plugin/dynamic-plugins/deployment.md @@ -7,9 +7,18 @@ Uploading a bundle plugin, enabling it, and managing permissions. Upload the built bundle to the host. Uses `multipart/form-data`. The `pluginId` in the URL must match the `id` in `plugin.json`. +Preferred (handles entry + chunks + maps + css automatically): + +```bash +TEABLE_ENDPOINT=http://localhost:3003 TEABLE_PAT= \ + node /root/deployments/inkreach-teable/packages/plugin-cli/dist/index.js deploy -d . +``` + +Raw curl equivalent (legacy single-file form still works): + ```bash PLUGIN_ID=$(python3 -c "import json; print(json.load(open('plugin.json'))['id'])") -curl -s -b "auth_session=$SESSION" \ +curl -s -H "Authorization: Bearer $TEABLE_PAT" \ -F "js=@dist/main.js" \ -F "css=@dist/styles.css" \ -F "manifest=@plugin.json" \ @@ -17,34 +26,51 @@ curl -s -b "auth_session=$SESSION" \ ``` **Auth note**: Supports both session cookies (`auth_session`) and Personal Access -Tokens (`Authorization: Bearer `). +Tokens (`Authorization: Bearer `). Runtime asset GETs +(`/api/plugin/:id/runtime/*`) are session-only (PAT gets 403 by design) — +verify those with a signed cookie, not a PAT. -**Fields:** -| Field | Required | Max size | Description | -|---|---|---|---| -| `js` | Yes | 5 MB | The IIFE bundle (`dist/main.js`) | -| `css` | No | 1 MB | Optional stylesheet (`dist/styles.css`) | -| `manifest` | Yes | 10 KB | `plugin.json` manifest | +**Fields** (v2 multi-asset, since 2026-10): -**Returns** `201` with `{ id, name, version, ... }` — the plugin ID to use for -enable and install. +| Field | Required | Max count | Max size | Description | +|---|---|---|---|---| +| `js` | Yes | 20 | 5 MB each | Entry + lazy chunks. Entry = the file whose name matches `manifest.entry` (a lone differently-named js file still works — legacy fallback) | +| `css` | No | 5 | 1 MB each | Stylesheets, concatenated under the `manifest.style` name | +| `map` | No | 20 | 5 MB each | Source maps, served later as `application/json` | +| `manifest` | Yes | 1 | 10 KB | `plugin.json` manifest | -**Registering a NEW plugin id**: `POST /api/plugin` (manifest as JSON body) — -**admin-only** (PAT gets 403 `restricted_resource`). Uploading a bundle for an -already-registered id only needs the `plugin_developer` whitelist. Deleting a -plugin (`DELETE /api/plugin/:id`) is likewise admin-only. PAT-only automation -should reuse existing registered ids, or ask an admin to register/delete. +- **Total cap 15 MB** across all assets. +- Chunk filenames must be **FLAT** (`x-.chunk.js`) — the multipart + transport strips directory parts from filenames. +- The bundle dir on disk (`/.assets/plugins/bundle//`) + is **wiped on every upload** — stale chunks are removed, and hand-patched + files on disk are discarded. + +**Returns** `{ pluginId, hash, size, assets }` — `assets` is the stored file +count (1 for legacy single-file uploads). + +**Registering a NEW plugin id**: `POST /api/plugin {"id": "", ...}` +— PAT-accessible (`@TokenAccess()` since 2026-10) and **custom ids require +the `plugin_developer` whitelist**. Without an explicit `id` the server +mints a `plgXXX` id, which CANNOT receive bundles (manifest id must equal +the DB id). Deleting a plugin (`DELETE /api/plugin/:id`) remains admin-only. **Security** (7-layer validation): -1. Authentication — valid session cookie required (401 if missing) +1. Authentication — valid session cookie or PAT required (401 if missing) 2. Developer whitelist — `plugin_developer` table lookup (403 if not whitelisted) 3. Rate limit — 10 uploads/min per user (429 if exceeded) -4. Size limits — JS ≤ 5 MB, CSS ≤ 1 MB, manifest ≤ 10 KB -5. Manifest validation — required fields, `id` regex `[a-z0-9-]+` -6. Content validation — JS must be IIFE format (prefix `!function`/`(()=>`), - rejects `