Commit Graph
70 Commits
Author SHA1 Message Date
yeuimu a3fa01e25f docs(skill): plugin bundle pipeline v2 (IIFE/多资产/热更新) 文档入仓 2026-10-14 00:20:56 +00:00
yeuimu c9694511a1 feat(cli): base/space 命令 + 执行守则 + eval harness 入仓 + 多轮 eval (0.1.9)
- 新命令: space list/create、base create/delete(实测通过,冒烟覆盖)
- SKILL.md §0 执行守则:完成全部步骤再答复/不谎报/分批/撤销指引
  (snapshot 无恢复 API,用 deactivate+节点改回)
- hint 补 429 限流与超时模式
- evals/ 入仓: 14+6 题 harness 参数化(EVAL_ROOT/EVAL_ENV/EVAL_MODEL/
  EVAL_CFG/auto_skill 触发模式)+ 多轮修正式 eval 与灵活判分
- 判分器加固: workflow list 错误负载不崩、官方 CLI 探测(command -v)不算使用
- 实测: auto 触发批(入仓版重跑) 11/11、99.0% 首试、100% ≤2 次;
  多轮修正 2/2+3/3(修改而非重建);冒烟 85 用例含 token 路由(低权会话)
- 运维事实入档: POST/DELETE /api/plugin 为管理员专属(PAT 403)
2026-09-30 08:12:16 +00:00
yeuimu 517dde9dee feat(skill): native-form 速查文档 + 路由行 (0.1.8)
- dynamic-plugins/native-form.md: 咨询题直答型(两档注入 API + 多步表单
  copy-paste 示例 + 边界),解决 eval-6 历史失败(agent 把咨询当开发、跑飞)
- SKILL.md 模块地图加 3b 行:问'能不能/怎么做原生表单'先读它,直接作答
- 实测:plugin 技能 N=3 全绿(P1 25/25, P2 25/25, P3 24/25),
  native-form-routing 三轮全过(此前两轮全败)
2026-09-30 07:16:33 +00:00
yeuimu d6a8bc4063 feat(cli): 错误提示行动 hint + 发布门禁 + 分发收尾 (0.1.7)
- 错误输出附 hint: 404 Invalid tableId / Workflow not found / 路由不存在 /
  401 / 403 / 非法选项值 / visibilityFilter 等 9 类模式,JSON 模式含 hint 字段
- prepublishOnly 门禁: build + test + 全命令冒烟(81 用例)
- skills/ 移除 evals/(判分标准不随技能分发) + scripts/sync-skills.sh 一键同步
- 冒烟建库固定在 agent-playground 专用空间(生产保护红线)
2026-09-30 06:19:48 +00:00
yeuimu e584b7ebbe feat(cli): 参数别名收尾 + 全命令冒烟套件 (0.1.6)
- field list/get、view list、preset list 接受 --table-id(位置参数之外)
- record get/list 接受 --take/--skip 别名(= --page-size/--page 的原始偏移)
- scripts/smoke-commands.sh: 全命令冒烟套件,一次性验证所有 CLI 命令
  无幻影路由/无 CLI 语法错/无 CLI 请求形状错(80/80 通过,含 8 种触发器、
  preset/secret/subscription/plugin 只读命令)
- 轨迹依据: iteration-4 中 agent 猜 --table-id/--take 被拒,别名后归零
2026-09-30 03:59:39 +00:00
yeuimu 8dacbf7f2d 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 组也反复踩坑)
2026-09-29 06:45:37 +00:00
yeuimu fbff682d2c docs: 精简 README 安装引导词 2026-09-29 03:30:48 +00:00
yeuimu 8654cd8f20 docs: README 补充 skills CLI 按需单装(--skill)与 PromptScript 无害报错说明(v0.1.3 同步) 2026-09-29 03:14:46 +00:00
yeuimu fbf97a1b46 docs: 安装方式只保留 npm 包,移除 gitea git+ 备选说法
README/SKILL.md 统一为 npm i -g @inkreach/inkreach-teable;
v0.1.2 同步进包
2026-09-29 02:57:21 +00:00
yeuimu 69f354c833 chore: v0.1.1 —— 同步 SKILL.md/README 安装文档进 npm 包 2026-09-29 02:43:45 +00:00
yeuimu 87490e9c6f feat: 发布 npm 公共包 @inkreach/inkreach-teable
- 包名 @inkreach/teable-cli → @inkreach/inkreach-teable(npm i -g 即装即用)
- bin 路径规范化 ./dist/bin.js → dist/bin.js(npm 11 发布校验要求)
- README / SKILL.md 安装命令改为 npm 包优先,gitea git+ 与离线方式降为备选

已发布:https://www.npmjs.com/package/@inkreach/inkreach-teable (v0.1.0)
验证:tarball 99KB/38 文件,registry 全新安装 → bin/版本/真实端点鉴权全通过
2026-09-29 02:22:19 +00:00
yeuimu abdf71a816 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 经
触发评估循环验证为原版最优,保持不变
2026-09-29 01:47:54 +00:00
yeuimu f2c2c9e399 feat(skill): inkreach-teable 按 skill-creator 评估流程迭代优化
- 路由表新增 select 字段写入规则(先查 choices/新值先加选项,typecast 静默失败)
- scheduledTime 期望管理:用户要具体时刻/星期须提前告知后端不支持
- 常见错误表补充 mutationApplied=false 静默失败条目
- 快速语法块补充 webhook 调用 URL 形状
- description 按触发评估循环结果重写(20 题评估集,保留测试集分数胜出)
- evals.json 更新为 11 题评估集(沙箱隔离、含 secrets/依赖/选项更新覆盖)

评估:Pi + opencode-go/deepseek-v4.1-flash,两轮迭代 28 次运行,
改进版 8 类任务近满分且耗时 -23%
2026-09-29 01:47:48 +00:00
yeuimu e09ed1bc0f feat: 收录 inkreach-teable-plugin skill;install.sh 安装全部技能
skills/ 现包含 inkreach-teable + inkreach-teable-plugin,
npx skills add 一并安装两个;install.sh 改为遍历安装 skills/*。
2026-09-23 06:23:46 +00:00
yeuimu d81d14bd52 docs(skill): 去除全部 localhost:3003 —— 一律使用公网地址 table.inkreach.cc
skill 面向公开分发,不应包含部署机内网地址。SKILL.md / cli-reference /
rest-api / instance-access 共 10 处替换;rest-api 双源表与 ssh 隧道
兜底整段删除(前端 rewrite 已让域名全功能可用)。
2026-09-23 05:54:15 +00:00
yeuimu 65437e59d4 docs: 引导词定稿——三步带说明,公网鉴权地址 2026-09-23 05:50:38 +00:00
yeuimu 2f8b89686f docs: 精简 README 引导词为纯命令 2026-09-23 05:46:28 +00:00
yeuimu be2750108e docs: README 引导词去掉 teable 兼容警告,路径统一为 ~/.agents/skills 2026-09-23 05:45:06 +00:00
yeuimu 8499de27e6 docs(skill): swagger is a curated registry — audit-verified gaps both ways
Cross-diff of all @Controller routes vs docs-json (486 vs 438 after shape
normalization): real undocumented routes include the entire authority-matrix
family (live 200, 0 doc entries), select-visibility, plugin runtime/proxy,
ShareDB socket internals, oauth-server, health; ~27 doc entries belong to
the enterprise backend and 404 on this CE instance. Completeness check =
controller grep, docs-json alone is not exhaustive.
2026-09-23 05:43:13 +00:00
yeuimu 20c1e48193 docs(skill): swagger now reachable on the public domain (frontend rewrite) 2026-09-23 05:39:01 +00:00
yeuimu c5e291a7cf fix(skill): public domain 404s swagger — origin-aware base URL + discovery
Verified live: https://table.inkreach.cc/docs-json and /docs → 404
(frontend doesn't proxy swagger paths), while /api/* IS proxied
(PAT auth → 200). Guide now: two-origin table (domain for API calls,
backend origin for swagger), ssh-tunnel fallback for remote machines,
backend-controller grep as last resort; instance-access.md connection
table gains the public-domain row.
2026-09-23 05:33:35 +00:00
yeuimu 3df9225a68 docs(skill): full-route honesty — ~362 backend paths, core documented, rest via /docs-json
Audit vs live swagger: guide covers the CLI-mirrored operational core;
added verified aggregation/row-count + a full inventory section pointing
to the unauthenticated /docs-json spec for the ~35 uncovered groups
(auth/session, user, spaces, share, attachments, import/export, trash,
templates, org/oauth/admin/billing, official webhooks, plugin UI…).
2026-09-23 05:29:11 +00:00
yeuimu 2530eea8b5 feat(skill): teach direct REST API usage — api-reference/rest-api.md
Comprehensive review found the skill taught CLI-only usage (just one
script-action callback snippet); nothing on calling the API directly.

New guide (verified against the live instance):
- base URL + Bearer PAT auth, Node v24 fetch header-drop warning
- full endpoint map mirrored from the CLI's HTTP layer (records/fields/
  tables/views/presets/workflow/secrets/executions/fire/sql/PAT/signup)
- canonical curl examples incl. typecast/fieldKeyType bodies, url-encoded
  filter, convert-for-options, workflow node PUT + activate, script-action
  callback via triggerInput._apiBaseUrl
- gotchas: typecast never creates choices (silent no-op), visibilityFilter
  400, PATCH field strips options, whole-graph node replace, search[] tuple

SKILL.md: module table row + routing rule wired to the new guide.
2026-09-23 05:20:14 +00:00
yeuimu 4a65294626 docs: README reduced to the agent bootstrap prompt only 2026-09-23 04:47:07 +00:00
yeuimu eefdceffe8 docs: npx-based install (verified) — CLI via git+https, skill via skills add
- CLI: npx git+https://... (ad-hoc) / npm i -g git+https://... (permanent)
- skill: npx -y skills add https://... -g -y --copy (installs to
  ~/.zcode/skills + ~/.claude/skills + ~/.agents/skills + ~40 agents)
- prefix trap documented: npm/npx NEED git+, skills add must NOT have it
- install.sh kept as offline/no-npm fallback
2026-09-23 04:39:15 +00:00
yeuimu b4d83f4f58 feat: npx/git-install support — prepare script + files whitelist
prepare builds dist on git-clone installs (npx/npm i -g from the gitea
URL); files whitelist overrides .gitignore so dist ships in the pack.
2026-09-23 04:33:17 +00:00
yeuimu 73e189fd97 docs: copy-paste agent bootstrap prompt (引导词) for fresh hosts/sessions 2026-09-23 04:08:49 +00:00
yeuimu 76ef02d659 refactor: move skill to skills/inkreach-teable (from .agents/skills/)
git mv rename (history preserved); install.sh + README updated.
The skill is consumed via user-level install (~/.zcode/skills), so
in-repo .agents discovery was unnecessary.
2026-09-23 04:07:39 +00:00
yeuimu b7eb065c20 fix(install): repo root is 4 levels up from scripts/, not 3
The path math bug predates the split (same wrong depth in the monorepo
version); it only surfaces now that the error branch is reachable.
2026-09-23 04:01:55 +00:00
yeuimu 2a186c9acc chore: standalone repo bootstrap after split from inkreach-teable monorepo
- untrack dist/, add .gitignore, add pnpm-lock.yaml
- inline tsconfig.base.json options (no monorepo extends)
- install.sh: CLI now at repo root; install skill to ~/.zcode/skills (user scope)
- fix stale paths in skill docs; 7 actions in help text; README
2026-09-23 04:01:22 +00:00
yeuimu a551fa5355 chore: merge view-preset live-sync + share-db drift reconcile (restores preset feature) 2026-09-19 01:56:22 +00:00
yeuimu 4d94635bb2 chore: merge hotfix/workflow-trigger-echo-dedup (self-echo trigger suppression) 2026-09-18 02:28:26 +00:00
yeuimu dc92b4cc8f fix(workflow): suppress self-echo trigger duplication on workflow-written records
One user edit cascaded into 3-5 workflow executions and hourly sync jobs
generated 1000+ wasted runs/week (7d: 15.7k of 18k executions of the
ticket-status workflow ended in skipped no-op guards). Root cause: workflow
scripts write records via REST with a runner-signed token, and record-event
triggers could not distinguish those writes from human edits — every script
PATCH re-triggered its own workflow.

Attribution chain (new):
- WorkflowTokenService signs {workflowId, executionId} into the runner token
- jwt.strategy lifts it into CLS workflowContext (User-type branch)
- all 13 automation.record.* emit sites tag payloads with writeSource
- the 4 record triggers drop SELF echoes (writeSource.workflowId === own id)
  unconditionally; cross-workflow writes keep firing by default (chaining is
  load-bearing: order-sync建档 -> status workflow stamps 待处理, ~142 real
  actions/week). Opt-in stronger dedup via trigger config
  skipWorkflowWrites: true.

Verified live: human edit (处理反馈=正在核实) -> exactly 1 execution, script
flipped 流转状态 待处理->处理中, zero follow-up echoes; restore writes likewise
1 execution each with skipped no-ops. Unit: 14 new tests, 318 adjacent green.
2026-09-18 02:28:11 +00:00
yeuimu 035384131c feat(view-preset): shared view presets with fork-on-edit semantics
Lands the view-preset / server-persisted personal-layer feature that has
been running in production, plus the hotfix for "the same preset shows
different filter configs to different users" (物流问题-已出轨迹 on
tblgsJTXhqwiDpsjeL5 / viwyszJmLQNOLhA9sh5).

Root cause of the divergence:
- usePersonalViewSync implicitly flushed any overlay edit by a
  view|update user INTO the shared preset row (last-writer-wins). Overlays
  are stale snapshots seeded at apply time, so one user's seed silently
  erased colleagues' filter changes (2026-09-17 02:55 the preset lost the
  流转角色=售后 condition), and active overlays never converged because
  preset updates do not propagate.

Fix (semantics: presets are shared definitions, edits are personal):
- fork-on-edit: the first user edit strips the seed markers from the
  overlay (toolbar switches to the personal config); edits only ever
  write the user's personal row
- shared presets change ONLY through the new explicit, confirmed
  "Update to current config" row action (canManage users)
- un-edited preset overlays auto-follow redefinitions: applyPreset stamps
  a presetVersion (preset.lastModifiedTime); when the preset list
  refetches with a newer row the overlay re-seeds. Edited overlays carry
  no markers and are never clobbered. Legacy rows without a version
  re-seed exactly once
- exits strip both seed markers from the persisted personal row

Also lands the pending WIP shipped with the deployed build: select-choice
visibility constraint, personal-view server sync + proxy, grid async
records scope/request tracking, CLI resource display, preset i18n (10
locales), view_preset migrations, docs/skill updates, .zcode gitignore.

Verified end-to-end on the live instance (headless chromium, real
session): apply -> edit flips toolbar to Personal with ZERO shared-row
PUTs; explicit update PUTs the shared row; API-side redefinition
auto-reseeds an un-edited overlay on the next list refetch.
2026-09-17 04:07:30 +00:00
yeuimu 1df830adc4 chore: cli subscription command registration + env and deploy docs
- inkreach-cli: subscription command in bin, built dist
- env.development: public origin, plugin service registry, cache
  provider=redis, cold retention days
- skill docs: subscription quick reference, deploy and ops notes
2026-08-22 02:44:16 +00:00
yeuimu 3f38914de0 feat(subscription): record subscription center with triggerOnEnter semantics
- Full subscription CRUD + digest/instant email scheduling
- filter-evaluator delegates to core evaluateFilterItem (all operators +
  date sub-modes with timezone, value normalization, fail-closed)
- triggerOnEnter: notify only on the transition INTO the filter condition
- filter field visibility validated on create/update
- CLI: subscription list/get/create/update/delete/events/send
2026-08-22 02:34:00 +00:00
yeuimu 19a9a9012f feat(field): lastModifiedTime expression field references
Parse LAST_MODIFIED_TIME([Name]/{fldId}) references into trackedFieldIds so
the modification time only resets when a referenced field changes.

- core: relax expression from z.literal('LAST_MODIFIED_TIME()') to z.string(),
  add parseLastModifiedTimeReferences() + spec
- v1 field-supplement.service: resolve trackedFieldIds from expression on
  create/convert (incl. same-type merge), 400 on unknown field name
- v2 field-open-api-v2.service: resolve trackedFieldIds from expression on
  create/createFields/convert
- docs: field-types.md tracked-field reference section
2026-08-20 14:31:20 +00:00
yeuimu 92e73f7baa fix(automation): fire record triggers on all grid write paths (paste/clear/duplicate)
Investigation of the 'grid edits bypass the REST layer' report found the
ShareDB socket submit path is dead code (adapter db.commit() throws and no
frontend submitOp callsites exist) — grid cell edits already flow through
record.updateCell -> REST updateRecord and DO fire triggers. The real
coverage gaps were the selection-layer writes that call the open-api
SERVICES below the controller where the manual emits live:

- v1 selection paste/paste-by-id, clear/clear-by-id (grid paste, Delete-key
  clear): emitted nothing. Now emit automation.record.create/update with
  full post-write records + changedFieldIds/oldFields diffs (best-effort —
  emission failure logs but never fails the write).
- v1 record duplicate: emitted nothing. Now emits automation.record.create.
- v2 paste endpoints (range paste + paste-stream + paste-by-id-stream):
  returned only counts. PasteResult/PasteStreamDoneEvent now carry per-record
  update diffs and created-record values through to the nest layer, which
  emits create/update events (full record + diff metadata).
- v2 record duplicate: now emits automation.record.create.

Form submissions intentionally remain formSubmitted-only (independent
channel); deletions have no trigger category. Docs corrected: grid cell
edits fire triggers (they are REST), coverage table updated.

Verified live: duplicate / range paste / stream paste on a v2 base each
produced a workflow execution with full triggerInput; selection e2e
114/114, PasteHandler specs 30/30, selection unit 31/31.
2026-08-18 18:54:52 +00:00
yeuimu 7281a4f2e6 docs: workspace packages must be rebuilt before the backend (silent stale-dist trap)
The backend never compiles @teable/* sources — it loads packages/*/dist via
node_modules symlinks (@teable/v2-core is a webpack external, read at runtime
from packages/v2/core/dist/index.cjs). Editing a package's src and only
rebuilding apps/nestjs-backend ships nothing: no error, old behavior online.
Documented the correct build order + verification greps in AGENTS.md and the
skill's development guide (found while shipping the defaultValue cascade fix).
2026-08-18 09:06:24 +00:00
yeuimu 4176165ec7 docs(skill): oldFields is top-level, not nested in record
E2E feedback: script following the documented 'record.oldFields.*'
nesting read undefined. Actual payload shape (verified in
workflow_execution.trigger_input + trigger node output):
triggerInput.oldFields = { fldXXX: value } at TOP level, sibling of
record. Align all trigger output docs (recordUpdated §2, recordCondition
§3, recordCreatedOrUpdated §4) + add explicit access examples
(triggerInput.oldFields?.['fldXXX'], {{n0.oldFields.fldXXX}}).
2026-08-18 08:22:45 +00:00
yeuimu 9ec7dcb15a docs(skill): warn --config must be flat action config
Wrapping --config in {"config":{...}} deploys a silent empty script
(succeeds in ~1ms, result {}). Document the trap + verification recipe
(workflow get --json, nodes[].config.code non-empty).
2026-08-18 07:49:51 +00:00
yeuimu 70f8372fbc fix(workflow): deliver changedFieldIds + oldFields to recordUpdated trigger
Engine gap: the 'automation.record.update' event carries changedFieldIds
(and now oldFields), but RecordUpdatedTrigger.accumulate() dropped both —
scripts had no way to see what changed or pre-update values, despite the
documented trigger output (trigger-types.md).

- record-updated/record-condition/record-created-or-updated triggers:
  pass changedFieldIds + oldFields through to accumulate()
- batch-buffer TriggerEvent += oldFields; batch flush records[] entries
  now carry changedFieldIds + oldFields per record
- v2 updateRecord/updateRecords: emit oldFields (pre-update snapshot)
- v1 controller updateRecord/updateRecords: snapshot before-fields via
  getRecordsById, compute changedFieldIds (deep-equal diff, same
  semantics as v2) instead of hardcoded [] — v1/v2 parity
- new spec record-updated.trigger.spec.ts (4 tests, passthrough +
  watch-filter + table isolation); workflow suite 16 files / 99 tests
- trigger-types.md: document semantics + honest scope (REST API writes;
  ShareDB grid edits do not fire this trigger)

Live-verified: PATCH 流转角色 → trigger_input.changedFieldIds=[fld...],
oldFields=[售前客服] → record=[技术]; script reads oldRole via
triggerInput; fieldIds watch-filter blocks non-watched fields.
2026-08-18 07:49:51 +00:00
yeuimu 80801346b5 fix(workflow): sendNotification skip-on-empty-toUserId + error precision + doc unification
- toUserId 解析为空时静默跳过 (succeeded, sentCount=0, skipped:true) 而非 failed
  → 支持'仅有重复订单时才通知'的条件通知编排
- 必填校验只列出实际缺失字段 (message),不再误报已解析的字段
- 文档统一变量格式: {{nodeId.path}} 为主,{{index.path}} 兼容
- spec 6/6,线上实测: 空收件人 skip + 非空正常发送均验证
2026-08-17 10:11:25 +00:00
yeuimu 146d62c80b docs(skill): document sendNotification action (7 actions total) + CLI support
- skill: SKILL.md (7 actions in description + routing table entry), action-types.md
  (new section #4 sendNotification: config keys, variables, system-in-app semantics),
  automation-guide.md/cli-reference.md (--to-user/--title/--message/--severity/--path),
  development.md (7 actions), metadata.json abstract
- cli: action add --type sendNotification now builds config from granular flags
  (toUserId/title/message/severity/path); ACTION_TYPES includes sendNotification;
  live-verified via workflow create + action add + delete
2026-08-17 07:50:15 +00:00
yeuimu 3a03e0753c fix(cli): read commander's camelCase keys for kebab-case flags
commander stores --code-file as opts.codeFile (kebab keys are never set),
so 'action update --code-file' silently no-opped (workflow.ts read
opts['code-file']) and 'auth token create --base-ids/--full-access'
never applied (auth.ts read the kebab keys). The 'action add' path was
unaffected because buildActionConfig's pick() checks both forms.

- workflow.ts action update: opts['code-file'] ?? opts.codeFile
- auth.ts token create: baseIds/fullAccess dual-form read
- verified live: action update --code-file now persists the new script
  (was silently keeping old code); commander dual-key behavior confirmed
  by direct parse (kebab keys absent, camelCase present, and this
  commander version drops the --base-ids VALUE entirely — the dual read
  covers both)
2026-08-16 17:47:17 +00:00
yeuimu e1a91dfa84 feat(cli+skill): make documented field UX real + fix skill doc drift
CLI (packages/inkreach-cli):
- new src/lib/field-normalize.ts: type aliases (sel/num/text/mul/…),
  select string-choices -> {name}, compact --fields 'Name:type:choices'
  expansion, smart type inference from --options shape / --name
  (--type now optional), name->ID resolution for foreignTableName /
  linkFieldName / lookupFieldName (incl. link-field foreignTableId
  backfill + unknown-key stripping), rating/number/button required-
  option defaults
- field create: +--description/--lookup-options/--is-lookup
- field update --options + table create --fields route through the
  normalizer; DEFAULT_ENDPOINT :3000 -> :3003
- 20 unit tests (field-normalize.spec.ts, vitest); live-verified:
  string choices, alias, foreignTableName, lookup-by-names, compact
  format, inference (priority->singleSelect, max:5->rating)

Skill docs (.agents/skills/inkreach-teable):
- ports :3000 -> :3003 everywhere (backend real port; :3000 is an
  unrelated container on this host)
- remove dead hr_admin account; note throwaway signup + real admin
- plugin register: actual URL-based flags; document plugin csp
  list/set; view/dashboard install requires config enable first
- field-types.md marked verified-2026-08 (aliases/inference/name
  resolution now implemented + tested)
- development.md: webpack dist/index.js cache trap; PGPASSWORD
  placeholder instead of wrong literal
2026-08-16 05:58:01 +00:00
yeuimu 5a984eb5f8 docs(skill): update inkreach-teable skill for fixed field routes + select-choice dependency
- cli-reference.md: field get/update/delete now <tableId> <fieldId>;
  new field visible-choices command; note --options goes via convert API
- tables-fields.md: correct update/delete signatures; options-rewrite
  semantics (convert API, whole-replace not merge); new section on
  select-choice visibilityFilter dependency (read/write/evaluate,
  backend 400 guards, write-time enforcement message)
2026-08-16 05:35:23 +00:00
yeuimu 10b52db8b2 fix(field): enforce select-visibility guard on the V2 convert path + repair inkreach-cli field routes
- FieldOpenApiV2Service.convertField now calls the shared V1 validator
  (assertSelectVisibilityConfigValid) before executing the update command.
  Previously only the V1 path validated self-reference / dependency cycles,
  so invalid configs could be persisted through V2 (verified live: a
  self-referencing choice was accepted with 200).
- V1 assertSelectVisibilityConfigValid private -> public (shared guard).
- Unit tests: 3 new cases in field-open-api-v2.service.spec.ts (guard
  invoked with exact args; rejects before executing when validator throws;
  called with raw type/options for non-select conversions). 63/63 pass;
  tsc build config 0 errors.
- inkreach-cli: field get/update/delete targeted non-existent /api/field/:id
  routes; now use /api/table/:tableId/field/:fieldId. update --options goes
  through the convert API (the PATCH update API strips options by design).
  New: convertField + getVisibleChoices client methods, field visible-choices
  command (evaluates choice visibility for a record).
2026-08-16 05:31:05 +00:00
yeuimu 4dc65c2a0c fix(test): update plugin-base-config spec to match findMany implementation
getEffectiveConfig was rewritten in 948a20b88 to fetch all configs via
findMany (Prisma cannot reliably WHERE on nullable String? with ''), but
the spec still mocked the old findFirst call chain — the table-override
test silently fell back to defaults. Mocks now return the config array.
2026-08-14 07:27:22 +00:00
yeuimu 07836c0165 feat(plugin): dynamic CSP connect-src from plugin manifests
Bundle plugins run inside the base page (not a sandboxed iframe), so their
declared apiOrigins must be in the PAGE's connect-src. Previously the page
CSP was hardcoded static — plugins connecting to new APIs required a code
change + rebuild.

- GET /api/plugin/csp-origins: union of all plugins' manifest apiOrigins
- [[...slug]].tsx getServerSideProps: fetches origins via ssrApi, merges
  with static fallbacks ('self' + 8001 hosts), builds CSP dynamically
- PUT /api/plugin/:pluginId accepts apiOrigins (merged into manifest JSON)
- CLI: plugin csp list / plugin csp set <id> --origins '[...]'
- @DisabledPermission on plugin GET/PUT routes (default-deny guard blocked
  PAT auth); ownership still enforced via manageablePluginCreatedBy()
2026-08-14 05:48:56 +00:00
yeuimu 5ed6569535 merge: plugin CLI commands + base mount + CSP apiOrigins + developer whitelist 2026-08-14 02:50:02 +00:00
yeuimu bc8c76851d docs(skill): add plugin module to SKILL.md + routing rules + description
- Module map: new Plugins row (list/install/uninstall, dev allow/revoke)
- Routing rules: 3 new plugin intents (custom UI, allow devs, external API)
- Description: mention dynamic plugins + dev whitelist + CSP
2026-08-13 15:39:56 +00:00
yeuimu 49836d2655 docs: add plugin CLI commands to skill reference 2026-08-13 15:29:41 +00:00
yeuimu 3eb66513ea feat(plugin): developer whitelist CLI + idempotent backend
- CLI: plugin dev list/allow/revoke (userId or email)
- Backend: PluginDeveloperService.addDeveloper is now idempotent
  (returns existing record instead of unique-constraint 500)
- Backend: removeDeveloper silently no-ops on P2025 (not found)
- Controller: route api/plugin-developers (avoids :pluginId collision),
  @DisabledPermission so admin check is the sole gate
2026-08-13 15:07:32 +00:00
yeuimu 1708e7997a feat: form-submitted trigger multi-form dropdown + skill/docs sync 2026-08-12 06:22:30 +00:00
yeuimu 73998173a8 docs(skill): update formSubmitted trigger docs for multi-form support
Reflect the formId(formId) -> formIds[] change across skill + CLI + docs:
- skill api-reference/trigger-types.md: formIds array config + legacy note
- skill guides (automation-guide.md, cli-reference.md): --form-ids <csv>
- inkreach-cli: add --form-ids option (csv parse), keep --form-id legacy
- docs/automation/triggers.md: P1 formSubmitted marked done
- docs/automation/README.md: formSubmitted config fields updated
2026-08-12 03:59:12 +00:00
yeuimu ef013690f3 feat(workflow): expose trigger user role to script actions
Scripts can now branch on WHO triggered the workflow and their role in the
base, without an extra API call (the workflow token has no role-read scope).

New triggerInput fields (user-initiated triggers only: buttonClick /
recordCreated / recordUpdated / recordCondition / formSubmitted):
- _triggerUserRoles: string[]  (enabled role display names in this base)
- _triggerUserIsAdmin: boolean (space owner / AM admin / AM-disabled)

Absent for scheduledTime / webhook (no user context).

Implementation:
- trigger-user-info.ts: resolveTriggerUserInfo(prisma, baseId, userId) —
  standalone, unit-tested (8 tests). Mirrors the admin logic in
  AuthorityMatrixPermissionService but returns only role names + bool.
- workflow-runner.service.ts: resolve once at first action node, merge into
  enrichedInput (both execute() and test() paths).
- skill action-types.md: document the fields + script branching example.
2026-08-11 10:08:12 +00:00
yeuimu cf25c8ff93 feat(cli): support --dependencies for script actions
- action add/update: new --dependencies <json> option
- parseDependencies accepts JSON, name@version shorthand, or bare names
- update is incremental; --dependencies replaces the whole array
- docs: deployment/systemd.md + skill SKILL/action-types/development
  document the npm-deps feature and the service-PATH requirement
  (spawn npm ENOENT without nvm bin dir on PATH)
2026-08-11 05:51:27 +00:00
yeuimu aff4d70b71 chore(cli): rebuild dist for search[] tuple fix 2026-08-07 18:44:54 +00:00
yeuimu e5975fe5bc docs(skill): add filter operator reference by field type
Add comprehensive operator-to-field-type mapping table covering:
- text/number/boolean/date base operators
- select/link/user field-specific operators (isAnyOf, hasAnyOf, etc.)
- date sub-operators (today, pastWeek, exactDate, etc.)
- conjunction (and/or) + nesting
- Warning: isAnyOf not valid for text fields — use OR conjunction
2026-08-07 18:00:37 +00:00
yeuimu 6527602272 fix(cli): search param uses search[] tuple format
The records API requires search[]=value (tuple), not search=value
(string). Without this, the CLI returned 400 'expected tuple, received
string'. Same fix already exists in the workflow getRecords action.
2026-08-07 17:55:03 +00:00
yeuimu e1d6b03a61 fix: getRecords action page→take/skip + prisma engine fix + CLI fixes
- getRecords automation action: translate page/pageSize to skip/take
  before calling the records API (which ignores page/pageSize, causing
  pagination to always return first 100 records)
- Add postbuild script to restore correct prisma engine binary after
  nest build (which generates an incompatible engine causing SIGSEGV)
- CLI fixes (from earlier commits):
  - deleteRecords: query params instead of JSON body
  - deleteTable: add /base/{baseId} prefix
2026-08-07 16:55:18 +00:00
yeuimu 0f3c88126c fix(cli): table delete uses correct route /api/base/{baseId}/table/{tableId}
CLI was sending DELETE /api/table/{tableId} but the only
controller is at api/base/:baseId/table, causing 404.
2026-08-07 10:55:16 +00:00
yeuimu be21be9465 fix(cli): batch record delete sends recordIds as query params
The API controller reads recordIds from @Query(), not @Body.
CLI was sending { records: ids } as JSON body, causing:
'expected array, received undefined at "recordIds"'

Fixed: rename deleteRecord→deleteRecords, send recordIds as
repeated query params (?recordIds=rec1&recordIds=rec2).
2026-08-07 10:32:25 +00:00
yeuimu 0891bda134 feat(cli): add inkreach-teable plugin commands
plugin list: list registered plugins (id/name/status/runtimeType)
plugin register: register by URL (--name --url --positions)
plugin config list: list base configs (pluginId/tableId/enabled/form/write)
plugin config enable/disable: enable/disable per table or base-wide

Added 5 client methods: getPlugins, registerPlugin, listPluginConfigs,
upsertPluginConfig for CRUD.
2026-08-06 09:03:04 +08:00
yeuimu 584b75ba69 fix(skill): trim inkreach-teable description under 1024-char limit
The skill description was 1033 chars, exceeding pi's 1024-char limit and
causing a 'Skill conflicts: description exceeds 1024 characters' warning,
which prevents the skill from loading/triggering reliably.

Shortened to 990 chars while preserving all trigger-relevant keywords
(inkreach-teable/Teable, ID patterns bseXXX/tblXXX/..., configure
tables/automations, dev/deploy, systemd). Also fixed YAML folded-scalar
artifacts that injected stray spaces after slashes
('/ formSubmitted', '/ httpRequest') by avoiding line breaks after '/'.
2026-08-05 09:22:26 +00:00
yeuimu 3e06a10fe0 skill(iter-1): fix CLI getRecords flag + docs from eval feedback
Ran 4 end-to-end evals (skill-creator flow): all 23 assertions passed (100%).
The evals exposed 3 issues, now fixed:

1. CLI bug: getRecords/createRecord/updateRecord --target-table flag was
   read as opts['target-table'] but commander stores it camelCase
   (opts.targetTable). Added a pick() helper accepting both forms.
2. trigger-types.md: buttonClick output said record.fields.* implying all
   fields; actually record.fields only contains the button field's count.
   Documented the back-channel API fetch pattern for other fields.
3. automation-guide.md: documented that 'workflow test' uses mock input
   with empty fields, so upstream outputs are absent in test mode — use
   real fire for data-dependent validation.

Also: corrected button field options (workflow.id object, not workflowId).
2026-08-05 10:48:33 +08:00
yeuimu d3790ab856 refactor: rename skill 'inkreach-teable-ops' -> 'inkreach-teable'
Rename the skill directory and update all references (SKILL.md frontmatter
name, AGENTS.md, design doc, install.sh). Git tracks this as a rename so
history is preserved.
2026-08-05 10:35:04 +08:00
yeuimu 3810c33d69 refactor: rename CLI command 'inkreach' -> 'inkreach-teable'
Rename the command (and its config namespace) to match the project name:

- bin:          inkreach        -> inkreach-teable
- program name: inkreach        -> inkreach-teable
- config dir:   ~/.inkreach/    -> ~/.inkreach-teable/
- env vars:     INKREACH_*      -> INKREACH_TEABLE_*
- all docs (SKILL.md, guides, api-reference, AGENTS.md, design doc, install.sh)

Unchanged (intentional):
- npm package name: @inkreach/teable-cli
- package directory: packages/inkreach-cli/
- skill name: inkreach-teable-ops
- organization/authors: 'inkreach' (npm scope @inkreach)

Existing config auto-migrated (~/.inkreach -> ~/.inkreach-teable). Verified
end-to-end: auth status, workflow list (636), build clean.
2026-08-05 10:14:26 +08:00
yeuimu 5fa843df05 feat: add inkreach CLI + inkreach-teable-ops skill
A self-built CLI and companion agent skill for operating a deployed
inkreach-teable instance — configuring tables, fields, records, views, and
the project's OWN automation engine.

Why not the official 'teable' CLI:
  inkreach-teable's automation is self-built with a different contract:
  - conditions (not filter), fieldIds (not watchFieldIds)
  - recordCondition (not recordMatchesConditions)
  - webhook route /api/webhook/{baseId}/{wfId} (not the /base//workflow/ form)
  - scheduledTime backend honors only timing.type + timing.interval
  The official CLI targets the upstream contract and 404s/misconfigures here.
  Table/field/record/view APIs ARE upstream-compatible, but automation is not.

inkreach CLI (packages/inkreach-cli/):
  - Node.js + commander, zero extra runtime deps
  - Uses node:http (NOT fetch — Node 24 fetch drops Authorization header)
  - Commands: auth (login/status/token), base/table/field/record/view,
    workflow (list/get/create/update/delete/activate/deactivate/test),
    trigger set (8 types, builds correct config keys), action add/update/delete
    (6 types), secret, runs (list/get/rerun), fire (button-click/webhook), sql
  - --json mode for agent parsing; GET->modify->PUT whole-graph for node edits
  - Verified end-to-end: create workflow -> set webhook trigger -> add script
    action -> activate -> fire webhook -> runs list (succeeded)

inkreach-teable-ops skill (.agents/skills/inkreach-teable-ops/):
  - SKILL.md entry: module map, routing rules, 11 key constraints (the real
    contract keys), execution rules, error recovery
  - guides/: cli-reference, automation-guide, tables-fields, instance-access,
    development (build/restart, known gotchas)
  - api-reference/: trigger-types (8 triggers, exact config), action-types
    (6 actions, sandbox globals), field-types (21 types incl. Button)
  - metadata.json + install.sh
2026-08-05 02:04:39 +08:00