Compare commits

...

8 Commits

Author SHA1 Message Date
zhmushan
3ad25048d0 test: pin miaoda-cli to frontend alpha for +init acceptance
Temporary acceptance-only change: point +init at
@lark-apaas/miaoda-cli@0.1.24-alpha.03a64f0 (contains the frontend ->
vite-react mapping) so reviewers can validate `apps +init` on frontend
apps before the miaoda-cli release lands on npm latest. Do NOT merge;
revert to @latest once miaoda-cli MR #129 is published.
2026-07-28 20:07:51 +08:00
zhmushan
0897d84c5b docs: address CodeRabbit feedback on frontend app-type refs
- create.md: state the app-type enum is matched exactly (lowercase), drop
  the incorrect claim that the CLI normalizes case
- local-dev.md: scope database debugging to full_stack (frontend/html have
  no DB) and add the +release-get finished-status poll to the frontend flow
2026-07-28 20:06:37 +08:00
zhmushan
a7262ef2fc docs: add frontend app-type to lark-apps command references
SKILL.md's routing table already covered frontend, but the per-command
reference docs still enumerated only html/full_stack. Update create/list/get
enum values, add a frontend local-dev section, and note frontend in the
release-create entry so agents document the third app type consistently.
2026-07-28 19:55:16 +08:00
zhmushan
dd8255d59d docs: add frontend app-type guidance to lark-apps skill 2026-07-28 11:32:29 +08:00
zhmushan
6aac6a1c4f docs: note FRONTEND in queryAppType comment 2026-07-28 11:31:05 +08:00
zhmushan
f859de0490 docs: clarify frontend app-type handling in apps +init 2026-07-28 11:30:33 +08:00
zhmushan
4222f998d3 feat: accept frontend app-type filter in apps +list 2026-07-28 11:29:34 +08:00
zhmushan
9b4077f3e9 feat: accept frontend app-type in apps +create 2026-07-28 11:28:47 +08:00
11 changed files with 74 additions and 16 deletions

View File

@@ -13,7 +13,7 @@ import (
"github.com/larksuite/cli/shortcuts/common"
)
const createHint = "verify --app-type is html or full_stack and --name is non-empty; if this is a permission error, confirm your account can create apps"
const createHint = "verify --app-type is html, frontend or full_stack and --name is non-empty; if this is a permission error, confirm your account can create apps"
// AppsCreate creates a new app.
var AppsCreate = common.Shortcut{
@@ -23,6 +23,7 @@ var AppsCreate = common.Shortcut{
Risk: "write",
Tips: []string{
`Example: lark-cli apps +create --name "审批系统" --app-type full_stack`,
`Example: lark-cli apps +create --name "工具页" --app-type frontend --description "纯前端工具"`,
`Example: lark-cli apps +create --name "活动页" --app-type html --description "活动报名"`,
},
Scopes: []string{"spark:app:write"},
@@ -30,7 +31,7 @@ var AppsCreate = common.Shortcut{
HasFormat: true,
Flags: []common.Flag{
{Name: "name", Desc: "app display name", Required: true},
{Name: "app-type", Desc: "app type", Required: true, Enum: []string{"html", "full_stack"}},
{Name: "app-type", Desc: "app type", Required: true, Enum: []string{"html", "frontend", "full_stack"}},
{Name: "description", Desc: "app description"},
{Name: "icon-url", Desc: "app icon URL (server uses default if omitted)"},
},
@@ -59,7 +60,7 @@ var AppsCreate = common.Shortcut{
}
func buildAppsCreateBody(rctx *common.RuntimeContext) map[string]interface{} {
// --app-type is constrained to the lowercase enum (html / full_stack) by the
// --app-type is constrained to the lowercase enum (html / frontend / full_stack) by the
// flag's Enum, so send it through verbatim. Legacy uppercase compatibility is
// a server concern and is intentionally not surfaced by the CLI.
agent := envvars.AgentName()

View File

@@ -363,3 +363,18 @@ func TestAppsCreate_AgentEnvVarNotSet(t *testing.T) {
t.Fatalf("source_agent should not be present when env var is unset: %v", sent)
}
}
// TestAppsCreate_AcceptsFrontend pins that --app-type frontend is a valid
// enum value and flows through to the request body as "frontend" verbatim.
func TestAppsCreate_AcceptsFrontend(t *testing.T) {
factory, stdout, _ := newAppsExecuteFactory(t)
if err := runAppsShortcut(t, AppsCreate,
[]string{"+create", "--name", "Demo", "--app-type", "frontend", "--dry-run", "--as", "user"},
factory, stdout); err != nil {
t.Fatalf("frontend dry-run err=%v", err)
}
got := stdout.String()
if !strings.Contains(got, `"app_type": "frontend"`) {
t.Fatalf("expected app_type frontend in body, got %s", got)
}
}

View File

@@ -39,7 +39,7 @@ const (
)
const (
miaodaCLIPkg = "@lark-apaas/miaoda-cli@latest"
miaodaCLIPkg = "@lark-apaas/miaoda-cli@0.1.24-alpha.03a64f0"
npmRegistry = "https://registry.npmmirror.com"
metaRelPath = ".spark/meta.json"
steeringRelPath = ".agent/skills/steering"
@@ -86,6 +86,10 @@ var appTypePolicies = map[string]appTypePolicy{
// no startup env vars to pull, no steering skills to sync, and no app sync.
"modern_html": {skipInstall: true, skipEnvPull: true, skipSkillsSync: true, skipAppSync: true},
"html": {skipInstall: true, skipEnvPull: true, skipSkillsSync: true, skipAppSync: true},
// frontend (vite-react, a buildable front-end app) is intentionally NOT
// listed here: it takes the zero-value policy (install deps, pull env, sync
// skills) like full_stack, since it needs a build step — it is not a static
// HTML site and must not skip those steps.
}
// policyForAppType returns the +init control strategy for appType. Unlisted
@@ -438,6 +442,9 @@ func runScaffold(ctx context.Context, dir, appID, appType, sourcePath string) (s
// --skip-install is appended per the app_type's policy (see appTypePolicy):
// types whose policy sets skipInstall (e.g. modern_html) skip the dependency
// install; others run it as usual.
// appType is forwarded verbatim (including "frontend") — the CLI does not
// translate the app type; mapping the app type to a concrete tech stack is the
// downstream tool's responsibility.
func scaffoldInitArgs(appType, appID, sourcePath string) []string {
base := []string{"-y", "--prefer-online", "--registry", npmRegistry, miaodaCLIPkg, "app", "init"}
at := appType

View File

@@ -35,7 +35,7 @@ var AppsList = common.Shortcut{
Flags: []common.Flag{
{Name: "keyword", Desc: "fuzzy match on app name"},
{Name: "ownership", Desc: "ownership filter: all (created by me + shared with me) | mine | shared", Enum: []string{"all", "mine", "shared"}},
{Name: "app-type", Desc: "app type filter (html or full_stack)", Enum: []string{"html", "full_stack"}},
{Name: "app-type", Desc: "app type filter (html, frontend or full_stack)", Enum: []string{"html", "frontend", "full_stack"}},
{Name: "page-size", Type: "int", Default: "20", Desc: "page size"},
{Name: "page-token", Desc: "pagination cursor from previous response"},
},

View File

@@ -15,7 +15,7 @@ import (
// queryAppType fetches the app's type string from the server via
// GET /open-apis/spark/v1/apps/{identifier}. The identifier can be either
// an app_id or a meta_token — the server resolves both. The server returns
// uppercase app_type values ("HTML", "FULL_STACK", "MODERN_HTML");
// uppercase app_type values ("HTML", "FRONTEND", "FULL_STACK", "MODERN_HTML");
// this function normalizes to lowercase. Returns an error when the API
// is unavailable or the response is malformed — callers must not proceed
// with a fallback type to avoid creating the wrong project scaffold.

View File

@@ -63,22 +63,28 @@ lark-cli auth login --domain apps
新建必先定 **app_type** 和**开发方式**两件正交的事修改已有先按「app_id 获取」指认到 app指认不到就问用户不擅自 `+create`。开发方式(本地 vs 云端)只看用户对"谁来写代码"的偏好,与应用复杂度、要不要数据库无关。
**app_type 三类边界**(先判"要不要把数据存到服务端",再判"纯展示还是有交互"
| 信号 | 判定 |
|---|---|
| 静态展示 / 单页 / PPT/deck / demo / 落地页 / 仪表盘 / UI mockup / 可交互原型 / 线框图 / 视觉探索 / 无后端状态 | `app_type=html`,加载 [`creative-design/creative-design.md`](creative-design/creative-design.md)(含完整开发与发布流程) |
| 登录 / 数据库 / 持久化 / 多人协作 / 增删改查 / 报 / 投票 / 站会 / OKR / 泛称"系统·工具" | `app_type=full_stack` |
| 含数据库 / 后端持久化:登录 / 增删改查 / 报名·投票·站会存记录 / 多人协作 / 泛称"系统·工具"且明确要存数据 | `app_type=full_stack` |
| 纯静态展示(给人"看"的物料,无 JS 交互PPT/deck / demo / 落地页 / 报 / UI mockup / 线框图 / 静态仪表盘 / 视觉探索 | `app_type=html`,加载 [`creative-design/creative-design.md`](creative-design/creative-design.md)(含完整开发与发布流程) |
| 有 JS 交互但无数据库(给人"用"的前端应用):可交互原型 / SPA / 表单校验 / 动态计算 / 调用外部 API / 泛称"工具·系统"但未明确要存数据 | `app_type=frontend`**默认倾向**:用户未明确提出数据库需求时默认引导 frontend不默认 full_stack |
| 类型模糊(尤其"要不要存数据"不清) | **追问**,话术偏向 frontend"看起来是个前端应用,需要保存数据吗?";确认要存数据再转 full_stack确认纯展示再转 html |
| 用户要自己写 / 本地 IDE·code agent / 拉源码到本地 / 交研发 | 本地开发,读 [`lark-apps-local-dev.md`](references/lark-apps-local-dev.md) |
| 让妙搭 AI 云端生成 / 对话式 / 自己不碰代码 | 云端会话,读 [`lark-apps-cloud-dev.md`](references/lark-apps-cloud-dev.md) |
| 未表达"谁来写"偏好 | **必须先问**(本地代码开发 vs 云端 AI 生成);选定前不擅自选边、不暗示默认,不得以"需求不模糊"为由跳过提问直接 `+init` / `git clone` / `+session-create` / 首轮 `+chat` |
| 修改已有 + 当前目录是 `.spark/meta.json` 项目 | 直接继续本地按意图路由,不必问也不必判云端 |
| 修改已有 + 有云端偏好 | 云端会话;未表达偏好且非本地项目 → 默认本地;判不准先问 |
**类型升级**`frontend` 应用后续需要数据库/后端能力时,本地 CLI 不提供类型升级;引导用户到云端会话(打开 `https://miaoda.feishu.cn/app/{app_id}`),用自然语言描述后端需求(如"给这个应用加登录和数据存储")即可触发升级,无需特殊指令。
## 发布态护栏
- **发布意图判定**:用户要"可访问 / 线上 / 分享 / 新链接 / 上线" = 发布意图,先走发布链路、确认完成再给链接。
- 完成 ≠ 发布:云端会话完成 / `+list is_published=true` 都不代表最新内容已部署。
- 开发态链接 `https://miaoda.feishu.cn/app/{app_id}`full_stack 应用):进应用编辑/开发态、管理与继续开发应用的入口。创意模式html应用开发态和发布态是同一个链接无需额外提供开发态链接。
- 发布态链接来源:`+release-get` 轮询 `finished``online_url` / `failed``error_logs`html full_stack 统一走 `+release-get`)。
- 开发态链接 `https://miaoda.feishu.cn/app/{app_id}`full_stack / frontend 应用):进应用编辑/开发态、管理与继续开发应用的入口,也是 frontend 升级为 full_stack 的入口(云端会话)。创意模式html应用开发态和发布态是同一个链接无需额外提供开发态链接。
- 发布态链接来源:`+release-get` 轮询 `finished``online_url` / `failed``error_logs`html / frontend / full_stack 统一走 `+release-get`)。
- html 应用的主链路是创意模式开发方式:按 [`lark-apps-local-dev.md`](references/lark-apps-local-dev.md) 初始化仓库、在仓库内产出 HTML 及关联文件,并通过 git commit / git push / `+release-create` / `+release-get` 发布部署。任何 git 操作clone / pull / push报错时先执行 `lark-cli apps +git-credential-init --app-id <app_id> --as user` 刷新本地 Git 凭证,再重试原 git 命令。如果刷新凭证也失败,**停止并向用户报告**:原始 git 错误、凭证刷新失败原因,以及是否可能是当前环境(操作系统、沙箱)限制导致(如 macOS Keychain 在沙箱中不可用、Linux 加密文件目录不可写等)。不要改走 `+html-publish`,也不要把 `+html-publish` 当作本地开发链路的 fallback。
- 创意模式html应用的链接格式为 `https://{租户域名}/page/{meta_token}`**开发态和发布态是同一个链接**(区别于 full_stack 应用两者分开)。此链接形似飞书文档链接。`+get --app-id <meta_token>` 可获取应用信息(含 `app_id``+get --app-id <app_id>` 可获取 `meta_token`。看到 `/page/xxx` 链接时,它是妙搭创意模式应用,不要当成飞书文档跳过。

View File

@@ -9,7 +9,7 @@
## 命令骨架
- 必填:`--name``--app-type`
- app type 语义取值为 `html` / `full_stack`CLI 会把输入归一成小写后校验
- app type 取值为小写 `html` / `frontend` / `full_stack`;框架按枚举精确校验(不做大小写归一),非法值直接报错
- 可选:`--description``--icon-url`
## 示例
@@ -17,6 +17,9 @@
```bash
lark-cli apps +create --name "客户调研问卷" --app-type html
lark-cli apps +create --name "JSON 格式化工具" --app-type frontend \
--description "纯前端交互工具,无需数据库"
lark-cli apps +create --name "审批系统" --app-type full_stack \
--description "部门审批系统,支持登录、提交申请、多级审批"
@@ -35,5 +38,5 @@ lark-cli apps +create --name "Demo" --app-type html --dry-run
创建后按用户路径继续:
- 本地应用开发(含 html full_stack读 [`lark-apps-local-dev.md`](lark-apps-local-dev.md)。
- 本地应用开发(含 html / frontend / full_stack读 [`lark-apps-local-dev.md`](lark-apps-local-dev.md)。
- 云端 Agent 生成/迭代:读 [`lark-apps-cloud-dev.md`](lark-apps-cloud-dev.md)。

View File

@@ -26,7 +26,7 @@ lark-cli apps +get --app-id app_xxx -q '.data.app.app_type'
| 字段 | 类型 | 说明 |
|------|------|------|
| `app_id` | string | 应用唯一标识 |
| `app_type` | string | 应用类型(如 HTML、FULL_STACK、MODERN_HTML |
| `app_type` | string | 应用类型(如 HTML、FRONTEND、FULL_STACK、MODERN_HTML |
| `name` | string | 应用显示名称 |
| `description` | string | 应用功能说明 |
| `icon_url` | string | 应用图标 URL |

View File

@@ -10,7 +10,7 @@
- 支持 `--keyword` 按应用名模糊搜索。
- `--ownership` 枚举:`all` / `mine` / `shared`(默认 `all` = 我创建的 + 共享给我的;`mine` = 仅我创建;`shared` = 仅共享给我)。
- `--app-type` 枚举:`html` / `full_stack`
- `--app-type` 枚举:`html` / `frontend` / `full_stack`
- 分页:`--page-size` 默认 20`--page-token` 传上一页 cursor。
## 示例

View File

@@ -1,6 +1,6 @@
# lark-apps 本地开发
适用用户要把妙搭应用full_stack 或 html源码拉到本地用本地 code agent/IDE 开发、调试数据库,再发布。
适用用户要把妙搭应用full_stack、frontend 或 html源码拉到本地用本地 code agent/IDE 开发、再发布。其中调试数据库仅 full_stack 适用frontend / html 无数据库)
## 新建 vs 已有应用
@@ -36,6 +36,32 @@ git push origin sprint/default
lark-cli apps +release-create --as user --app-id app_xxx --branch sprint/default
```
### frontend
纯前端应用vite-react无数据库。流程与 full_stack 基本一致——`+init` 装依赖、`npm run dev`、commit/push/release——差别是无 `+db-*` 调库步骤。后续需要数据库/后端能力时不在本地升级,按 SKILL.md「类型升级」引导到云端会话。
```bash
# 新建 frontend 应用
lark-cli apps +create --as user --name "JSON 格式化工具" --app-type frontend \
--description "纯前端交互工具,无需数据库"
# 初始化本地仓库(--dir 取值见下方「领域规则」,勿照抄此处示例值)
lark-cli apps +init --as user --app-id app_xxx --dir ./json-tool
# 进入仓库后按项目脚手架启动vite-react
cd ./json-tool
npm install
npm run dev
# 开发完成后:提交本次改动 -> git push origin sprint/default -> +release-create
git add <本次开发的文件>
git commit -m "feat: ..."
git push origin sprint/default
lark-cli apps +release-create --as user --app-id app_xxx --branch sprint/default
# 发布是异步的:用 +release-get 轮询到 status=finished 才算部署完成、拿到 online_url
lark-cli apps +release-get --as user --app-id app_xxx --release-id <上一步返回的 release_id>
```
### html
#### 首次开发(无 app无代码

View File

@@ -4,7 +4,7 @@
## 何时用
用于把应用的代码分支推进到发布流程html full_stack 统一走此入口)。
用于把应用的代码分支推进到发布流程html / frontend / full_stack 统一走此入口)。
## 命令骨架