feat(skill): inkreach-teable-plugin 按 skill-creator 评估流程迭代优化

- 导入规则精确化:@teable/* 禁运行时值导入(import type 可);
  react 仅在 webpack externals 映射到宿主全局时允许
- 新增效率守则:信任技能文档、知识题不读宿主源码、不上传演示插件验证
- 修复失效引用:hr_admin 死账号、404 测试基地 → 改为一次性沙箱基地模式
- 补充认证提示:管理类路由(如 DELETE /api/plugin)PAT 403 需 session 回退
- deployment.md 部署脚本改用 PAT 鉴权与必填 BASE_ID

评估:6 题 × 2 轮 24 次运行(含真实上传/清理链路);description 经
触发评估循环验证为原版最优,保持不变
This commit is contained in:
yeuimu
2026-09-29 01:47:54 +00:00
parent f2c2c9e399
commit abdf71a816
2 changed files with 44 additions and 28 deletions
+29 -10
View File
@@ -13,7 +13,7 @@ description: >-
"I need a dashboard widget" — or wants the native form embedded or "I need a dashboard widget" — or wants the native form embedded or
extended inside a plugin (原生表单, 表单注入, 表单虚拟字段, custom form extended inside a plugin (原生表单, 表单注入, 表单虚拟字段, custom form
layout, NativeForm, NativeFormScope, form panel) — load this skill. layout, NativeForm, NativeFormScope, form panel) — load this skill.
Reference example at plugins/batch-edit-pro/. Reference example at `plugins/batch-edit-pro/` (on the deployment host: `/root/deployments/inkreach-teable/plugins/batch-edit-pro/`).
--- ---
# inkreach-teable dynamic plugin development # inkreach-teable dynamic plugin development
@@ -28,13 +28,27 @@ JS that executes in the host page and accesses host APIs through
> `POST /api/plugin/:id/upload-bundle`. It runs in the same React tree as the host > `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. > and has full access to the host's SDK, UI library, and OpenAPI helpers.
> >
> **Critical constraint**: NEVER `import` from `@teable/sdk`, `@teable/ui-lib`, > **Critical constraint**: no RUNTIME imports from `@teable/sdk`, `@teable/ui-lib`,
> `@teable/openapi` or `react` in plugin code. These are mapped to > `@teable/openapi` — webpack maps them to `window.__TEABLE_HOST__.*` externals, so a
> `window.__TEABLE_HOST__.*` by webpack externals at build time — importing > value import compiles to `undefined` at runtime. `import type { X } from '@teable/...'`
> them produces `undefined` at runtime. Always read from the host global: > 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 > `const { useFields } = window.__TEABLE_HOST__.SDK`. See
> `dynamic-plugins/troubleshooting.md` Pitfall 1 for details and symptoms. > `dynamic-plugins/troubleshooting.md` Pitfall 1 for details and symptoms.
## 0. Two efficiency rules (read first)
- **Trust these docs.** The `dynamic-plugins/` documents are the verified contract
for this deployment. Answer plugin capability/behavior questions directly from
them — do NOT re-derive behavior by spelunking `nestjs-backend/src` or the host
source tree; that burns minutes and adds nothing. Source diving is only for
debugging a concrete discrepancy the docs can't explain.
- **Don't upload demo plugins to verify knowledge.** For how-to/consultation
questions, a written answer (with code) is the deliverable. Only upload a bundle
when the user actually asked to deploy — test uploads belong in a throwaway
sandbox base, never the production plugin registry.
## 1. When to use this skill ## 1. When to use this skill
- User says "写个插件 / 做个插件 / make a plugin" — yes. - User says "写个插件 / 做个插件 / make a plugin" — yes.
@@ -73,13 +87,18 @@ JS that executes in the host page and accesses host APIs through
- Endpoint: `http://localhost:3003` (backend; `:3000` is taken by an unrelated - Endpoint: `http://localhost:3003` (backend; `:3000` is taken by an unrelated
docker container on this host — see AGENTS.md), frontend `http://localhost:3002`. docker container on this host — see AGENTS.md), frontend `http://localhost:3002`.
- Test account: create a throwaway via `POST /api/auth/signup` — the old - Plugin upload requires a `plugin_developer` row for the uploading user
`hr_admin@xingchen.tech` account no longer exists on this instance. Plugin
upload additionally requires a `plugin_developer` row for the uploading user
(`INSERT INTO plugin_developer (id, user_id, email, created_time, created_by) (`INSERT INTO plugin_developer (id, user_id, email, created_time, created_by)
VALUES ('plgd<name>', '<userId>', '<email>', now(), '<userId>')`). VALUES ('plgd<name>', '<userId>', '<email>', now(), '<userId>')`).
- Test base: `bseAi6Hazmw7jPWZbfO`, test table: `tbldtYd4ZhvkcyAaada`. - **Sandbox base for any hands-on test**: create a throwaway base
- Auth: session cookie `auth_session`, or `inkreach-teable auth login`. (`POST /api/base` with `{"spaceId":"<spaceId>","name":"Sandbox(可删)"}`),
do all table/install testing inside it, and delete it (`DELETE /api/base/:id`)
when done. Never hardcode base/table IDs here — they rot; resolve them at
runtime (`inkreach-teable base list`) instead.
- Auth: PAT (`Authorization: Bearer <PAT>`; upload-bundle supports it) or
session cookie `auth_session`. Note: some admin-gated routes (e.g.
`DELETE /api/plugin/:id`) pass with an admin session but 403 with a plain PAT
even for the same user — fall back to the session for those.
## 5. Start here ## 5. Start here
@@ -144,16 +144,15 @@ VALUES (gen_random_uuid(), '<userId>', '<email>', '<adminUserId>');
## Getting a session cookie ## Getting a session cookie
Prefer a PAT (upload-bundle accepts `Authorization: Bearer <PAT>`). For a
session cookie, sign in with any real account created for the purpose
(`POST /api/auth/signup`), then add its `plugin_developer` row before uploading:
```bash ```bash
curl -s -c /tmp/cookie_hr.txt http://localhost:3003/api/auth/signin \ curl -s -c /tmp/cookie.txt http://localhost:3003/api/auth/signin \
-X POST -H 'Content-Type: application/json' \ -X POST -H 'Content-Type: application/json' \
-d '{"email":"hr_admin@xingchen.tech","password":"Xingchen@2026"}' -d '{"email":"<real-account>","password":"<password>"}'
SESSION=$(grep auth_session /tmp/cookie.txt | awk '{print $NF}')
> Account note (2026-08): `hr_admin@xingchen.tech` no longer exists on the
> deployed instance. Create a throwaway user via `POST /api/auth/signup`
> and add a `plugin_developer` row for it before uploading (see SKILL.md).
SESSION=$(grep auth_session /tmp/cookie_hr.txt | awk '{print $NF}')
``` ```
## Full deploy script (copy-paste) ## Full deploy script (copy-paste)
@@ -163,22 +162,20 @@ SESSION=$(grep auth_session /tmp/cookie_hr.txt | awk '{print $NF}')
set -e set -e
DIR="${1:-.}" DIR="${1:-.}"
UPLOAD_URL="${UPLOAD_URL:-http://localhost:3003}" UPLOAD_URL="${UPLOAD_URL:-http://localhost:3003}"
BASE_ID="${BASE_ID:-bseAi6Hazmw7jPWZbfO}" BASE_ID="${BASE_ID:?set to a throwaway sandbox base, never a production base}"
# Read plugin ID from manifest # Read plugin ID from manifest
PLUGIN_ID=$(python3 -c "import json; print(json.load(open('$DIR/plugin.json'))['id'])") PLUGIN_ID=$(python3 -c "import json; print(json.load(open('$DIR/plugin.json'))['id'])")
# Get session # Get session
SESSION=$(grep auth_session /tmp/cookie_hr.txt 2>/dev/null | awk '{print $NF}') # PAT preferred (upload-bundle accepts Bearer auth):
if [ -z "$SESSION" ]; then AUTH=(-H "Authorization: Bearer ${PAT:?set PAT env}")
curl -s -c /tmp/cookie_hr.txt "$UPLOAD_URL/api/auth/signin" \ # ...or session:
-X POST -H 'Content-Type: application/json' \ SESSION=$(grep auth_session /tmp/cookie.txt 2>/dev/null | awk '{print $NF}')
-d '{"email":"hr_admin@xingchen.tech","password":"Xingchen@2026"}' > /dev/null AUTH=(-b "auth_session=$SESSION")
SESSION=$(grep auth_session /tmp/cookie_hr.txt | awk '{print $NF}')
fi
# Upload (URL includes pluginId from manifest) # Upload (URL includes pluginId from manifest)
curl -s -b "auth_session=$SESSION" \ curl -s "${AUTH[@]}" \
-F "js=@$DIR/dist/main.js" \ -F "js=@$DIR/dist/main.js" \
-F "css=@$DIR/dist/styles.css" \ -F "css=@$DIR/dist/styles.css" \
-F "manifest=@$DIR/plugin.json" \ -F "manifest=@$DIR/plugin.json" \
@@ -186,7 +183,7 @@ curl -s -b "auth_session=$SESSION" \
echo "Uploaded: $PLUGIN_ID" echo "Uploaded: $PLUGIN_ID"
# Enable # Enable
curl -s -b "auth_session=$SESSION" -X PUT \ curl -s "${AUTH[@]}" -X PUT \
"$UPLOAD_URL/api/base/$BASE_ID/plugin-config/$PLUGIN_ID" \ "$UPLOAD_URL/api/base/$BASE_ID/plugin-config/$PLUGIN_ID" \
-H 'Content-Type: application/json' \ -H 'Content-Type: application/json' \
-d '{"enabled":true,"tableIds":[]}' > /dev/null -d '{"enabled":true,"tableIds":[]}' > /dev/null