From abdf71a8164d84d73bcb2e0e9b9aaab22630925c Mon Sep 17 00:00:00 2001 From: yeuimu <2197651308@qq.com> Date: Tue, 29 Sep 2026 01:47:54 +0000 Subject: [PATCH] =?UTF-8?q?feat(skill):=20inkreach-teable-plugin=20?= =?UTF-8?q?=E6=8C=89=20skill-creator=20=E8=AF=84=E4=BC=B0=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E8=BF=AD=E4=BB=A3=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 导入规则精确化:@teable/* 禁运行时值导入(import type 可); react 仅在 webpack externals 映射到宿主全局时允许 - 新增效率守则:信任技能文档、知识题不读宿主源码、不上传演示插件验证 - 修复失效引用:hr_admin 死账号、404 测试基地 → 改为一次性沙箱基地模式 - 补充认证提示:管理类路由(如 DELETE /api/plugin)PAT 403 需 session 回退 - deployment.md 部署脚本改用 PAT 鉴权与必填 BASE_ID 评估:6 题 × 2 轮 24 次运行(含真实上传/清理链路);description 经 触发评估循环验证为原版最优,保持不变 --- skills/inkreach-teable-plugin/SKILL.md | 39 ++++++++++++++----- .../dynamic-plugins/deployment.md | 33 +++++++--------- 2 files changed, 44 insertions(+), 28 deletions(-) diff --git a/skills/inkreach-teable-plugin/SKILL.md b/skills/inkreach-teable-plugin/SKILL.md index 3e8ee5e..5cd6f15 100644 --- a/skills/inkreach-teable-plugin/SKILL.md +++ b/skills/inkreach-teable-plugin/SKILL.md @@ -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', '', '', now(), '')`). -- 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":"","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 `; 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 diff --git a/skills/inkreach-teable-plugin/dynamic-plugins/deployment.md b/skills/inkreach-teable-plugin/dynamic-plugins/deployment.md index bf76d16..05ad2e4 100644 --- a/skills/inkreach-teable-plugin/dynamic-plugins/deployment.md +++ b/skills/inkreach-teable-plugin/dynamic-plugins/deployment.md @@ -144,16 +144,15 @@ VALUES (gen_random_uuid(), '', '', ''); ## Getting a session cookie +Prefer a PAT (upload-bundle accepts `Authorization: Bearer `). 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":"","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