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:
@@ -13,7 +13,7 @@ description: >-
|
||||
"I need a dashboard widget" — or wants the native form embedded or
|
||||
extended inside a plugin (原生表单, 表单注入, 表单虚拟字段, custom form
|
||||
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
|
||||
@@ -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
|
||||
> and has full access to the host's SDK, UI library, and OpenAPI helpers.
|
||||
>
|
||||
> **Critical constraint**: NEVER `import` from `@teable/sdk`, `@teable/ui-lib`,
|
||||
> `@teable/openapi` or `react` in plugin code. These are mapped to
|
||||
> `window.__TEABLE_HOST__.*` by webpack externals at build time — importing
|
||||
> them produces `undefined` at runtime. Always read from the host global:
|
||||
> **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
|
||||
> `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
|
||||
|
||||
- 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
|
||||
docker container on this host — see AGENTS.md), frontend `http://localhost:3002`.
|
||||
- Test account: create a throwaway via `POST /api/auth/signup` — the old
|
||||
`hr_admin@xingchen.tech` account no longer exists on this instance. Plugin
|
||||
upload additionally requires a `plugin_developer` row for the uploading user
|
||||
- Plugin upload requires a `plugin_developer` row for the uploading user
|
||||
(`INSERT INTO plugin_developer (id, user_id, email, created_time, created_by)
|
||||
VALUES ('plgd<name>', '<userId>', '<email>', now(), '<userId>')`).
|
||||
- Test base: `bseAi6Hazmw7jPWZbfO`, test table: `tbldtYd4ZhvkcyAaada`.
|
||||
- Auth: session cookie `auth_session`, or `inkreach-teable auth login`.
|
||||
- **Sandbox base for any hands-on test**: create a throwaway base
|
||||
(`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
|
||||
|
||||
|
||||
@@ -144,16 +144,15 @@ VALUES (gen_random_uuid(), '<userId>', '<email>', '<adminUserId>');
|
||||
|
||||
## 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
|
||||
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' \
|
||||
-d '{"email":"hr_admin@xingchen.tech","password":"Xingchen@2026"}'
|
||||
|
||||
|
||||
> 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}')
|
||||
-d '{"email":"<real-account>","password":"<password>"}'
|
||||
SESSION=$(grep auth_session /tmp/cookie.txt | awk '{print $NF}')
|
||||
```
|
||||
|
||||
## Full deploy script (copy-paste)
|
||||
@@ -163,22 +162,20 @@ SESSION=$(grep auth_session /tmp/cookie_hr.txt | awk '{print $NF}')
|
||||
set -e
|
||||
DIR="${1:-.}"
|
||||
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
|
||||
PLUGIN_ID=$(python3 -c "import json; print(json.load(open('$DIR/plugin.json'))['id'])")
|
||||
|
||||
# Get session
|
||||
SESSION=$(grep auth_session /tmp/cookie_hr.txt 2>/dev/null | awk '{print $NF}')
|
||||
if [ -z "$SESSION" ]; then
|
||||
curl -s -c /tmp/cookie_hr.txt "$UPLOAD_URL/api/auth/signin" \
|
||||
-X POST -H 'Content-Type: application/json' \
|
||||
-d '{"email":"hr_admin@xingchen.tech","password":"Xingchen@2026"}' > /dev/null
|
||||
SESSION=$(grep auth_session /tmp/cookie_hr.txt | awk '{print $NF}')
|
||||
fi
|
||||
# PAT preferred (upload-bundle accepts Bearer auth):
|
||||
AUTH=(-H "Authorization: Bearer ${PAT:?set PAT env}")
|
||||
# ...or session:
|
||||
SESSION=$(grep auth_session /tmp/cookie.txt 2>/dev/null | awk '{print $NF}')
|
||||
AUTH=(-b "auth_session=$SESSION")
|
||||
|
||||
# Upload (URL includes pluginId from manifest)
|
||||
curl -s -b "auth_session=$SESSION" \
|
||||
curl -s "${AUTH[@]}" \
|
||||
-F "js=@$DIR/dist/main.js" \
|
||||
-F "css=@$DIR/dist/styles.css" \
|
||||
-F "manifest=@$DIR/plugin.json" \
|
||||
@@ -186,7 +183,7 @@ curl -s -b "auth_session=$SESSION" \
|
||||
echo "Uploaded: $PLUGIN_ID"
|
||||
|
||||
# Enable
|
||||
curl -s -b "auth_session=$SESSION" -X PUT \
|
||||
curl -s "${AUTH[@]}" -X PUT \
|
||||
"$UPLOAD_URL/api/base/$BASE_ID/plugin-config/$PLUGIN_ID" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"enabled":true,"tableIds":[]}' > /dev/null
|
||||
|
||||
Reference in New Issue
Block a user