@lark-project/meegle 0.0.16 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/README.md +322 -45
- package/README.zh-CN.md +312 -45
- package/THIRD_PARTY_NOTICES.md +76 -0
- package/bin/meegle-darwin-arm64 +0 -0
- package/bin/meegle-darwin-x64 +0 -0
- package/bin/meegle-linux-arm64 +0 -0
- package/bin/meegle-linux-x64 +0 -0
- package/bin/meegle-win32-arm64.exe +0 -0
- package/bin/meegle-win32-x64.exe +0 -0
- package/bin/meegle.js +24 -4
- package/package.json +6 -6
- package/third_party_licenses/github.com/cespare/xxhash/v2/LICENSE.txt +22 -0
- package/third_party_licenses/github.com/clipperhouse/displaywidth/LICENSE +21 -0
- package/third_party_licenses/github.com/clipperhouse/uax29/v2/graphemes/LICENSE +21 -0
- package/third_party_licenses/github.com/fatih/color/LICENSE.md +20 -0
- package/third_party_licenses/github.com/mattn/go-colorable/LICENSE +21 -0
- package/third_party_licenses/github.com/mattn/go-isatty/LICENSE +9 -0
- package/third_party_licenses/github.com/mattn/go-runewidth/LICENSE +21 -0
- package/third_party_licenses/github.com/mdp/qrterminal/v3/LICENSE +19 -0
- package/third_party_licenses/github.com/olekukonko/cat/LICENSE +21 -0
- package/third_party_licenses/github.com/olekukonko/errors/LICENSE +21 -0
- package/third_party_licenses/github.com/olekukonko/ll/LICENSE +21 -0
- package/third_party_licenses/github.com/olekukonko/tablewriter/LICENSE.md +19 -0
- package/third_party_licenses/github.com/pkg/browser/LICENSE +23 -0
- package/third_party_licenses/github.com/spf13/cobra/LICENSE.txt +174 -0
- package/third_party_licenses/github.com/spf13/pflag/LICENSE +28 -0
- package/third_party_licenses/golang.org/x/crypto/pbkdf2/LICENSE +27 -0
- package/third_party_licenses/golang.org/x/sys/unix/LICENSE +27 -0
- package/third_party_licenses/golang.org/x/term/LICENSE +27 -0
- package/third_party_licenses/gopkg.in/yaml.v3/LICENSE +50 -0
- package/third_party_licenses/gopkg.in/yaml.v3/NOTICE +13 -0
- package/third_party_licenses/rsc.io/qr/LICENSE +27 -0
package/README.zh-CN.md
CHANGED
|
@@ -1,21 +1,62 @@
|
|
|
1
1
|
# Meegle CLI
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://opensource.org/licenses/MIT)
|
|
4
|
+
[](https://nodejs.org/)
|
|
5
|
+
[](https://www.npmjs.com/package/@lark-project/meegle)
|
|
6
|
+
|
|
7
|
+
[English](./README.md) | [简体中文](./README.zh-CN.md)
|
|
4
8
|
|
|
5
9
|
飞书项目([Meegle](https://meegle.com?utm_source=github&utm_medium=readme&utm_campaign=meegle_cli) / [Lark Project](https://project.feishu.cn?utm_source=github&utm_medium=readme&utm_campaign=meegle_cli))命令行工具。在终端中管理工作项、查看排期、搜索数据,无需打开浏览器。
|
|
6
10
|
|
|
11
|
+
[安装](#安装) · [快速开始](#快速开始人类用户) · [Agent Skill](#ai-agent-skill) · [命令](#命令一览) · [认证](#认证) · [配置](#配置) · [安全](#安全与风险提示) · [贡献](#贡献)
|
|
12
|
+
|
|
13
|
+
## 为什么选择 Meegle CLI?
|
|
14
|
+
|
|
15
|
+
- **Agent 友好** — 附带一份 [AI Agent Skill](#ai-agent-skill),一条命令即可把 Meegle 操作手册喂给 Trae、Claude Code、Cursor、Windsurf、Gemini CLI 等主流 Agent。CLI 命令同时面向人类和 Agent 设计,结构化 JSON 输出、`--dry-run` 预览、`--device-code` 无 TTY 流程
|
|
16
|
+
- **覆盖完整** — 12 个业务域(工作项、工作流、子任务、评论、工时、关联、我的工作、视图、图表、团队、用户、空间),40+ 命令映射到 Meegle 核心能力
|
|
17
|
+
- **两层参数模型** — 日常用 `--flag-name` 轻便直接,复杂载荷(如 `fields[]`)用 `--params <json>` 兜底 —— 按场景选择合适粒度
|
|
18
|
+
- **输出格式灵活** — 支持 `json` / `table` / `ndjson` / `raw`,配合 `--select` 点路径投影可直接 pipe 给其他工具
|
|
19
|
+
- **默认安全** — 凭证存进系统 keychain、`${VAR}` 环境变量模板让 secret 不落地到 config 文件、多 profile 分离 staging / prod
|
|
20
|
+
|
|
21
|
+
## 功能概览
|
|
22
|
+
|
|
23
|
+
| 域 | 能力 |
|
|
24
|
+
| -------------------------------------------------- | ---------------------------------------------------------------------------------------- |
|
|
25
|
+
| 📋 [工作项](#workitem--工作项域) | 创建、查看、更新、批量读、MQL 查询、查看操作记录、读元数据 |
|
|
26
|
+
| 🔀 [工作流](#workflow--工作流域) | 节点和状态流转、更新节点字段、列出可用流转和必填字段 |
|
|
27
|
+
| ✅ [子任务](#subtask--子任务) | 创建、更新、完成、回滚子任务 |
|
|
28
|
+
| 💬 [评论](#comment--评论域) | 在工作项上添加和列出评论 |
|
|
29
|
+
| ⏱️ [工时](#workhour--工时域) | 列出工时记录、查看团队成员排期 |
|
|
30
|
+
| 🔗 [关联](#relation--关系域) | 列出关联工作项、查看关联类型定义 |
|
|
31
|
+
| 📌 [我的工作](#mywork--工作台域) | 查看本周 / 已完成 / 逾期待办 |
|
|
32
|
+
| 👁️ [视图](#view--视图域) | 创建和更新固定视图、按名称搜索视图 |
|
|
33
|
+
| 📊 [图表](#chart--度量域) | 列出视图下的图表、查看图表详情 |
|
|
34
|
+
| 👥 [团队与用户](#team--user--人员域) | 列出团队、团队成员、搜索用户、查看当前登录身份 |
|
|
35
|
+
| 🗂️ [空间](#project--空间域) | 按关键词搜索空间 |
|
|
36
|
+
| 🔐 [认证与配置](#认证) | OAuth 登录、Device Code 流程、多 profile 配置、环境变量注入 |
|
|
37
|
+
| 🤖 [Agent Skill](#ai-agent-skill) | 内置 skill 供 Trae / Claude Code / Cursor / Windsurf / Gemini / Copilot 使用 |
|
|
38
|
+
|
|
7
39
|
## 安装
|
|
8
40
|
|
|
41
|
+
### 前置条件
|
|
42
|
+
|
|
43
|
+
- Node.js >= 16(自带 `npm` / `npx`)
|
|
44
|
+
|
|
45
|
+
### 安装命令
|
|
46
|
+
|
|
9
47
|
```bash
|
|
10
48
|
npm install -g @lark-project/meegle
|
|
11
49
|
```
|
|
12
50
|
|
|
13
|
-
|
|
51
|
+
## 快速开始(人类用户)
|
|
14
52
|
|
|
15
|
-
|
|
53
|
+
> **给 AI Agent 的提示:** 如果你是在替用户完成这套安装的 AI Agent,请直接跳到 [快速开始(AI Agent)](#快速开始ai-agent--ci--无头环境) —— 那里有你需要的非交互步骤。
|
|
16
54
|
|
|
17
55
|
```bash
|
|
18
|
-
#
|
|
56
|
+
# (可选)持久化 host,后续登录就不用再选菜单
|
|
57
|
+
meegle config set host <host>
|
|
58
|
+
|
|
59
|
+
# 1. 登录(上下选择 host + 浏览器 OAuth)
|
|
19
60
|
meegle auth login
|
|
20
61
|
|
|
21
62
|
# 2. 查看本周待办
|
|
@@ -29,6 +70,85 @@ meegle workitem --help
|
|
|
29
70
|
meegle inspect workitem.create
|
|
30
71
|
```
|
|
31
72
|
|
|
73
|
+
## 快速开始(AI Agent / CI / 无头环境)
|
|
74
|
+
|
|
75
|
+
`meegle auth login` 默认走上下选择菜单 + 浏览器 OAuth 回调,依赖真实 TTY,在 CI Runner、管道、Claude Code 这类没有 TTY 的环境里会挂起或报错。这些场景请改用 Device Code 流程 —— 命令会输出授权 URL,让用户在浏览器里完成授权。
|
|
76
|
+
|
|
77
|
+
**Step 1 — 安装 CLI**
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
npm install -g @lark-project/meegle
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Step 2 — 持久化 host**
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
meegle config set host <host>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`<host>` 示例:`project.feishu.cn`、`meegle.com`,或自建租户域名(如 `your-tenant.example.com`)。
|
|
90
|
+
|
|
91
|
+
**Step 3 — Device Code 登录**
|
|
92
|
+
|
|
93
|
+
> 建议后台执行。命令会输出授权 URL —— 提取后发给用户,用户在浏览器完成授权后命令自动退出。
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
meegle auth login --device-code
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
如不想持久化 host,也可以每次显式传入:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
meegle auth login --device-code --host <host>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**Step 4 — 验证**
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
meegle auth status
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
完全无人值守的 CI(没有真人参与)请改用环境变量注入 token,参见 [沙盒 / CI](#沙盒--ci直接注入环境变量)。
|
|
112
|
+
|
|
113
|
+
## AI Agent Skill
|
|
114
|
+
|
|
115
|
+
`skills/meegle/` 是一份可直接加载的 **skill**,用来教 AI Agent —— Trae、Claude Code、Cursor、Windsurf、Gemini CLI、GitHub Copilot CLI —— 通过本 CLI 操作 Meegle。它把命令目录、MQL 语法、字段值写法、富文本 Markdown 规则,以及常见写入流程的 SOP 全部打包好。
|
|
116
|
+
|
|
117
|
+
### 安装
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
# 先装 CLI(skill 底层会调用 meegle 命令)
|
|
121
|
+
npm install -g @lark-project/meegle
|
|
122
|
+
|
|
123
|
+
# 再加载 skill —— 会自动探测已装的 Agent,并在各自的 skill 目录里登记
|
|
124
|
+
npx skills add larksuite/meegle-cli -y -g
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
`npx skills add` 会从仓库读取 `skills/meegle/SKILL.md`,写入本机所有 Agent CLI 的 skill 目录。每次升级重跑一遍即可拉取最新内容。
|
|
128
|
+
|
|
129
|
+
### 覆盖内容
|
|
130
|
+
|
|
131
|
+
- **命令参考** — 每个 `meegle` resource / method 的必填参数与示例
|
|
132
|
+
- **MQL 搜索** — `workitem query` 的语法、运算符、作用域关键字
|
|
133
|
+
- **字段值** — 复杂字段载荷(数组、嵌套 JSON、日期区间)的写法
|
|
134
|
+
- **富文本** — Meegle 富文本编辑器支持的 Markdown 子集
|
|
135
|
+
- **SOP** — 创建工作项、节点流转、状态流转、更新字段等场景的分步剧本
|
|
136
|
+
- **授权守护** — 所有业务命令前强制先过 `meegle auth status`
|
|
137
|
+
|
|
138
|
+
完整内容见 [skills/meegle/SKILL.md](./skills/meegle/SKILL.md) 和 [skills/meegle/references/](./skills/meegle/references/)。
|
|
139
|
+
|
|
140
|
+
### 使用方式
|
|
141
|
+
|
|
142
|
+
安装后直接用自然语言和 Agent 对话。例如 Trae:
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
帮我看一下 PROJ 空间本周待办里的 P0 需求
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Agent 会参考 skill,自动选择合适的 `meegle` 命令执行。配合 `--dry-run`(见 [安全](#安全与风险提示))可以在 Agent 真正执行副作用前先预览请求。
|
|
149
|
+
|
|
150
|
+
> 小提示:skill 的名字 `meegle` 和 CLI 二进制同名。文档里提到 **`meegle` skill** 时指 `skills/meegle/` 里的文件;提到 **`meegle` CLI** 时指你 PATH 上的 `meegle` 命令。
|
|
151
|
+
|
|
32
152
|
## 命令一览
|
|
33
153
|
|
|
34
154
|
### workitem — 工作项域
|
|
@@ -37,6 +157,7 @@ meegle inspect workitem.create
|
|
|
37
157
|
|------|------|
|
|
38
158
|
| `workitem create` | 创建工作项 |
|
|
39
159
|
| `workitem get` | 查看工作项概况 |
|
|
160
|
+
| `workitem +batch-get` | 按 ID 批量读取工作项(客户端侧在 `workitem get` 之上做扇出;`+` 前缀代表场景/语法糖命令) |
|
|
40
161
|
| `workitem update` | 修改工作项字段 |
|
|
41
162
|
| `workitem query` | 使用 MQL 搜索工作项 |
|
|
42
163
|
| `workitem list-op-records` | 查看操作记录 |
|
|
@@ -172,18 +293,56 @@ meegle workitem get --work-item-id 12345
|
|
|
172
293
|
meegle workflow get-node --work-item-id 12345 --need-sub-task
|
|
173
294
|
```
|
|
174
295
|
|
|
296
|
+
### 批量读取工作项
|
|
297
|
+
|
|
298
|
+
`workitem +batch-get` 会对每个 ID 分别调用 `workitem get` 并把结果聚合成一条响应。
|
|
299
|
+
共享 flag(如 `--project-key`)会应用到每一次 per-item 调用上。命令以 `+` 开头,
|
|
300
|
+
表示它是客户端侧的场景/语法糖命令,没有 1:1 对应的 MCP 工具,CLI 会在 `get` 之
|
|
301
|
+
上组合出批量语义。
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
# 在一次调用里传入逗号分隔的多个 ID
|
|
305
|
+
meegle workitem +batch-get --project-key PROJ --work-item-ids "12345,12346,12347"
|
|
306
|
+
|
|
307
|
+
# 从文件读取 ID(每行一个,以 '#' 开头的行视为注释)
|
|
308
|
+
meegle workitem +batch-get --project-key PROJ --ids-file ./ids.txt
|
|
309
|
+
|
|
310
|
+
# 每个 item 输出一行 JSON,summary 作为最后一行
|
|
311
|
+
meegle workitem +batch-get --project-key PROJ --work-item-ids "12345,12346" -o ndjson
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
响应结构(JSON):
|
|
315
|
+
|
|
316
|
+
```json
|
|
317
|
+
{
|
|
318
|
+
"summary": { "total": 3, "succeeded": 2, "failed": 1 },
|
|
319
|
+
"results": [
|
|
320
|
+
{ "work_item_id": 12345, "data": { /* ... */ } },
|
|
321
|
+
{ "work_item_id": 12346, "data": { /* ... */ } },
|
|
322
|
+
{ "work_item_id": 12347, "error": { "code": "...", "message": "..." } }
|
|
323
|
+
]
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
约束:单次调用最多 200 个 ID,固定 3 worker 并发。部分失败**不会**中断整批——
|
|
328
|
+
请通过 `summary.failed` 或 per-item 的 `error` 字段判断;服务端返回 401 会终止整批。
|
|
329
|
+
|
|
175
330
|
### 创建工作项
|
|
176
331
|
|
|
177
332
|
```bash
|
|
178
|
-
#
|
|
333
|
+
# 通过 --params 传 fields[]
|
|
179
334
|
meegle workitem create --project-key PROJ --work-item-type story \
|
|
180
|
-
--
|
|
181
|
-
|
|
335
|
+
--params '{"fields":[
|
|
336
|
+
{"field_key":"name","field_value":"优化登录流程"},
|
|
337
|
+
{"field_key":"priority","field_value":"P1"}
|
|
338
|
+
]}'
|
|
182
339
|
|
|
183
|
-
#
|
|
340
|
+
# 复杂字段值(数组、嵌套 JSON)也走 --params
|
|
184
341
|
meegle workitem create --project-key PROJ --work-item-type story \
|
|
185
|
-
--
|
|
186
|
-
|
|
342
|
+
--params '{"fields":[
|
|
343
|
+
{"field_key":"name","field_value":"排期任务"},
|
|
344
|
+
{"field_key":"schedule","field_value":[1722182400000,1722355199999]}
|
|
345
|
+
]}'
|
|
187
346
|
```
|
|
188
347
|
|
|
189
348
|
### 修改字段
|
|
@@ -191,12 +350,14 @@ meegle workitem create --project-key PROJ --work-item-type story \
|
|
|
191
350
|
```bash
|
|
192
351
|
# 修改工作项名称
|
|
193
352
|
meegle workitem update --work-item-id 12345 \
|
|
194
|
-
--
|
|
353
|
+
--params '{"fields":[{"field_key":"name","field_value":"新标题"}]}'
|
|
195
354
|
|
|
196
355
|
# 同时修改多个字段
|
|
197
356
|
meegle workitem update --work-item-id 12345 \
|
|
198
|
-
--
|
|
199
|
-
|
|
357
|
+
--params '{"fields":[
|
|
358
|
+
{"field_key":"name","field_value":"新标题"},
|
|
359
|
+
{"field_key":"priority","field_value":"P0"}
|
|
360
|
+
]}'
|
|
200
361
|
```
|
|
201
362
|
|
|
202
363
|
### MQL 搜索
|
|
@@ -232,19 +393,31 @@ meegle user search --user-keys "张三,李四" --project-key PROJ
|
|
|
232
393
|
meegle workitem get --work-item-id 12345 --project-key PROJ
|
|
233
394
|
```
|
|
234
395
|
|
|
235
|
-
###
|
|
396
|
+
### 写 `fields[]`(写入命令)
|
|
236
397
|
|
|
237
|
-
`workitem create`、`workitem update`、`workflow update-node`、`subtask update`
|
|
398
|
+
`workitem create`、`workitem update`、`workflow update-node`、`subtask update` 需要传 `fields[]`,通过 `--params` 走:
|
|
238
399
|
|
|
239
400
|
```bash
|
|
240
|
-
--
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
--set role='{"role":"RD"}' # JSON 对象
|
|
401
|
+
--params '{"fields":[
|
|
402
|
+
{"field_key":"name","field_value":"标题"},
|
|
403
|
+
{"field_key":"priority","field_value":"P1"}
|
|
404
|
+
]}'
|
|
245
405
|
```
|
|
246
406
|
|
|
247
|
-
|
|
407
|
+
### --set key=value(通用)
|
|
408
|
+
|
|
409
|
+
`--set` 是普通 flag 的**替代写法**,只影响**顶层参数**:`--set key=value` 等价于 `--key value`。适合在脚本里用统一的 key=value 语法,或通过 dot-path 写嵌套顶层对象。值自动类型推断(int / float / bool / string)。
|
|
410
|
+
|
|
411
|
+
```bash
|
|
412
|
+
# 下面两种等价:
|
|
413
|
+
meegle mywork todo --action this_week --page-num 1
|
|
414
|
+
meegle mywork todo --set action=this_week --set page_num=1
|
|
415
|
+
|
|
416
|
+
# dot-path 嵌套(Meegle 很少用到,但支持):
|
|
417
|
+
--set extra.flag=true # 变成 {"extra":{"flag":true}}
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
`--set` 只写**顶层参数**,**不会**写到工作项的 `fields[]`。写 `fields[]` 请用 `--params '{"fields":[...]}'`(见下)。
|
|
248
421
|
|
|
249
422
|
### --params JSON(兜底)
|
|
250
423
|
|
|
@@ -259,8 +432,8 @@ meegle workitem create --project-key PROJ --work-item-type story \
|
|
|
259
432
|
|
|
260
433
|
当 `--set`、`--params` 和普通 flag 同时使用时:
|
|
261
434
|
|
|
262
|
-
1.
|
|
263
|
-
2.
|
|
435
|
+
1. 普通 CLI flag 覆盖 `--params` / `--set` 中同名顶层 key
|
|
436
|
+
2. `--set` 覆盖 `--params` 中同名顶层 key
|
|
264
437
|
|
|
265
438
|
### 数组参数
|
|
266
439
|
|
|
@@ -283,7 +456,7 @@ meegle workflow get-node --work-item-id 12345 --need-sub-task
|
|
|
283
456
|
|
|
284
457
|
| Flag | 缩写 | 说明 |
|
|
285
458
|
|------|------|------|
|
|
286
|
-
| `--format` | `-o` | 输出格式:`json`(默认)、`table`、`ndjson`、`
|
|
459
|
+
| `--format` | `-o` | 输出格式:`json`(默认)、`table`、`ndjson`、`raw` |
|
|
287
460
|
| `--select` | | 字段投影(支持 dot path) |
|
|
288
461
|
| `--set` | | 设置嵌套参数(可重复) |
|
|
289
462
|
| `--params` | `-P` | 完整 JSON 参数体 |
|
|
@@ -291,47 +464,62 @@ meegle workflow get-node --work-item-id 12345 --need-sub-task
|
|
|
291
464
|
| `--verbose` | `-v` | 详细输出 |
|
|
292
465
|
| `--profile` | | 指定配置 profile |
|
|
293
466
|
|
|
294
|
-
##
|
|
467
|
+
## 进阶用法
|
|
468
|
+
|
|
469
|
+
### 输出格式
|
|
295
470
|
|
|
296
471
|
```bash
|
|
297
472
|
# JSON(默认)
|
|
298
473
|
meegle workitem get --work-item-id 12345
|
|
299
474
|
|
|
300
|
-
# 选取输出属性(支持 dot path)
|
|
301
|
-
meegle workitem get --work-item-id 12345 --select "id,name,status"
|
|
302
|
-
meegle mywork todo --action done --page-num 1 --select "list.work_item_info.work_item_name"
|
|
303
|
-
|
|
304
475
|
# NDJSON(适合管道处理)
|
|
305
476
|
meegle mywork todo --action this_week --page-num 1 -o ndjson
|
|
306
477
|
|
|
307
478
|
# 表格
|
|
308
479
|
meegle mywork todo --action this_week --page-num 1 -o table
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
### `--select` 字段投影
|
|
309
483
|
|
|
310
|
-
|
|
311
|
-
meegle workitem get --work-item-id 12345 -o yaml
|
|
484
|
+
`--select` 按 `.` 分段做字段投影;数组之后的下一段会对数组中每个元素广播(broadcast)剩余路径,并保留原来的嵌套结构。
|
|
312
485
|
|
|
313
|
-
|
|
314
|
-
|
|
486
|
+
| 表达式 | 响应 | 投影结果 |
|
|
487
|
+
|---|---|---|
|
|
488
|
+
| `list` | `{"list":[{"a":1}], "total":1}` | `{"list":[{"a":1}]}` |
|
|
489
|
+
| `list.a` | `{"list":[{"a":1,"b":2},{"a":3,"b":4}]}` | `{"list":[{"a":1},{"a":3}]}` |
|
|
490
|
+
| `list.a,list.b` | 同上 | `{"list":[{"a":1,"b":2},{"a":3,"b":4}]}`(多路径按下标合并) |
|
|
491
|
+
| `list.work_item_info.work_item_name` | `{"list":[{"work_item_info":{"work_item_name":"x"}}]}` | `{"list":[{"work_item_info":{"work_item_name":"x"}}]}` |
|
|
492
|
+
| `nodes.0` | `{"nodes":[{"id":"a"},{"id":"b"}]}` | `{"nodes":{"0":{"id":"a"}}}`(数字段按索引) |
|
|
315
493
|
|
|
316
|
-
|
|
317
|
-
|
|
494
|
+
```bash
|
|
495
|
+
# 顶层选择
|
|
496
|
+
meegle workitem get --work-item-id 12345 --select "id,name,status"
|
|
497
|
+
|
|
498
|
+
# 跨数组广播 —— 从嵌套记录里提取字段
|
|
499
|
+
meegle mywork todo --action done --page-num 1 \
|
|
500
|
+
--select "list.work_item_info.work_item_name,list.state_info.end_state_key_name"
|
|
501
|
+
|
|
502
|
+
# 顶层元数据 + 广播混合 —— total 和投影后的 list 记录会同时保留
|
|
503
|
+
meegle mywork todo --action done --page-num 1 \
|
|
504
|
+
--select "total,list.work_item_info.work_item_name"
|
|
318
505
|
```
|
|
319
506
|
|
|
320
|
-
###
|
|
507
|
+
### 元数据保留
|
|
321
508
|
|
|
322
|
-
`--select`
|
|
509
|
+
默认渲染下,响应的完整结构在所有 `--format` 中都会保留:列表接口返回的 `{"list":[...], "total":N, "pagination":{...}}` 原样呈现 —— 即使你没有在 `--select` 中点名,`total` / `pagination` 这些元数据字段依然可见。要钻到具体记录就显式用 `--select`(配合上面的广播语法)。`--format table` 和 `--format ndjson` 下,形如 `{"list":[...]}` 的单键包装(没有兄弟元数据)仍然会被无损展开成行展示。
|
|
323
510
|
|
|
324
|
-
|
|
325
|
-
# 从嵌套结构中提取字段
|
|
326
|
-
--select "list.work_item_info.work_item_name,list.state_info.end_state_key_name"
|
|
511
|
+
### Dry Run
|
|
327
512
|
|
|
328
|
-
|
|
329
|
-
|
|
513
|
+
有副作用的命令先用 `--dry-run` 预览请求再执行:
|
|
514
|
+
|
|
515
|
+
```bash
|
|
516
|
+
meegle workitem create --project-key PROJ --work-item-type story \
|
|
517
|
+
--params '{"fields":[{"field_key":"name","field_value":"测试"}]}' --dry-run
|
|
330
518
|
```
|
|
331
519
|
|
|
332
|
-
|
|
520
|
+
### 命令内省
|
|
333
521
|
|
|
334
|
-
|
|
522
|
+
用 `inspect` 查看任意命令的完整参数信息:
|
|
335
523
|
|
|
336
524
|
```bash
|
|
337
525
|
# 列出所有命令
|
|
@@ -394,6 +582,57 @@ meegle config get host
|
|
|
394
582
|
| 字段 | 说明 | 示例 |
|
|
395
583
|
|------|------|------|
|
|
396
584
|
| `host` | 站点域名 | `project.feishu.cn`、`meegle.com` |
|
|
585
|
+
| `user_access_token` | 用户访问令牌,支持 `${VAR}` 从环境变量读取 | `${CI_MEEGLE_TOKEN}` |
|
|
586
|
+
| `access_token_header` | 自定义承载 token 的 HTTP header 名;置空则用默认的 `Authorization: Bearer <token>` | `x-meegle-auth` |
|
|
587
|
+
| `user_agent` | 追加到 `User-Agent` 的 caller 段(形如 `meegle-cli/<ver> <user_agent>`);支持 `${VAR}` 模板;被环境变量 `MEEGLE_USER_AGENT` 覆盖 | `my-service/1.0` |
|
|
588
|
+
|
|
589
|
+
### 沙盒 / CI:直接注入环境变量
|
|
590
|
+
|
|
591
|
+
`MEEGLE_HOST` 和 `MEEGLE_USER_ACCESS_TOKEN` 两个约定名环境变量会在 CLI 启动时被直接读取,覆盖 profile 中的同名字段,无需任何 `config set`:
|
|
592
|
+
|
|
593
|
+
```bash
|
|
594
|
+
export MEEGLE_HOST=project.feishu.cn
|
|
595
|
+
export MEEGLE_USER_ACCESS_TOKEN=<your-user-token>
|
|
596
|
+
export MEEGLE_USER_AGENT=ci-runner # 可选;追加到 User-Agent,优先级高于 config.user_agent
|
|
597
|
+
meegle workitem get-brief --work_item_id 123
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
任一变量可以单独设置。走这个路径时 CLI 不访问 keychain,401 错误不会自动 refresh,由调用方自行轮转。
|
|
601
|
+
|
|
602
|
+
### 自定义 Auth Header
|
|
603
|
+
|
|
604
|
+
默认情况下 token 通过标准 `Authorization: Bearer <token>` 发送。若后端要求把 token 放到别的 header 里(且不允许带 `Authorization`),用 `access_token_header` 切换:
|
|
605
|
+
|
|
606
|
+
```bash
|
|
607
|
+
meegle config set access_token_header x-meegle-auth
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
或通过环境变量临时覆盖:
|
|
611
|
+
|
|
612
|
+
```bash
|
|
613
|
+
export MEEGLE_ACCESS_TOKEN_HEADER=x-meegle-auth
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
启用后 CLI 会以 `<header>: <token>` 发送裸值(无 `Bearer ` 前缀),并且**完全不发送** `Authorization` header —— 适用于对两种 header 共存敏感的后端。
|
|
617
|
+
|
|
618
|
+
### 环境变量模板
|
|
619
|
+
|
|
620
|
+
如果平台使用的环境变量名不是 `MEEGLE_*`,可以在 config.json 中用 `${VAR}` 占位符绑定任意字符串字段,运行时会用同名环境变量的值替换。这样既能避免在 config.json 里明文写敏感值,也能适配不同容器/CI 平台各自的变量命名习惯。
|
|
621
|
+
|
|
622
|
+
```json
|
|
623
|
+
{
|
|
624
|
+
"current": "prod",
|
|
625
|
+
"profiles": {
|
|
626
|
+
"prod": { "host": "project.feishu.cn", "user_access_token": "${PROD_CI_TOKEN}" },
|
|
627
|
+
"staging": { "host": "staging.feishu.cn", "user_access_token": "${STAGING_CI_TOKEN}" }
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
规则:
|
|
633
|
+
- 仅识别**整串形态**的占位符。`"${X}"` 会被展开;`"Bearer ${X}"` 按字面量处理,不展开。
|
|
634
|
+
- 引用的环境变量未设置或为空时 CLI **fail fast**,错误信息会带上字段路径和变量名。
|
|
635
|
+
- 当配置了 `user_access_token` 时,它会覆盖 `meegle auth login` 在本地 keychain 里写入的令牌。由于这种模式下没有本地 refresh 能力,服务端返回 401 时需要自行轮转环境变量值。
|
|
397
636
|
|
|
398
637
|
### 多环境 Profile
|
|
399
638
|
|
|
@@ -431,6 +670,34 @@ meegle auth login
|
|
|
431
670
|
|
|
432
671
|
命令列表会自动缓存,过期后在后台静默刷新,不影响使用。
|
|
433
672
|
|
|
673
|
+
## 安全与风险提示
|
|
674
|
+
|
|
675
|
+
本工具被设计为可由 AI Agent 调用来自动化 Meegle 操作,这本身就带有模型幻觉、执行不可控、提示词注入等固有风险。一旦你授予了 Meegle 访问权限,Agent 就会在授权范围内以你的用户身份行事,可能完成字段更新、状态流转、工作项创建等高影响操作,使用前请充分评估。
|
|
676
|
+
|
|
677
|
+
推荐的风险控制手段:
|
|
678
|
+
|
|
679
|
+
- 有副作用的命令先用 `--dry-run` 预览请求再正式执行
|
|
680
|
+
- 给 Agent 专用 profile(`meegle config profile create`),便于单独审计和吊销
|
|
681
|
+
- CI 或共享环境优先使用短期环境变量 token(`MEEGLE_USER_ACCESS_TOKEN`),401 时自行轮换;不要放松默认安全设置
|
|
682
|
+
|
|
683
|
+
使用本工具即视为你自愿承担由此带来的全部相关责任。
|
|
684
|
+
|
|
685
|
+
## Star History
|
|
686
|
+
|
|
687
|
+
[](https://star-history.com/#larksuite/meegle-cli&Date)
|
|
688
|
+
|
|
689
|
+
## 贡献
|
|
690
|
+
|
|
691
|
+
欢迎社区贡献。Bug 反馈或功能建议请提 [Issue](https://github.com/larksuite/meegle-cli/issues) 或 [Pull Request](https://github.com/larksuite/meegle-cli/pulls)。对于较大改动,建议先通过 Issue 讨论。
|
|
692
|
+
|
|
434
693
|
## License
|
|
435
694
|
|
|
436
|
-
MIT
|
|
695
|
+
本项目基于 **MIT 许可证** 开源。
|
|
696
|
+
|
|
697
|
+
该软件运行时会调用 Lark/飞书开放平台的 API,使用这些 API 需要遵守如下协议和隐私政策:
|
|
698
|
+
|
|
699
|
+
- [飞书用户服务协议](https://www.feishu.cn/terms)
|
|
700
|
+
- [飞书隐私政策](https://www.feishu.cn/privacy)
|
|
701
|
+
- [飞书开放平台应用服务商安全管理规范](https://open.feishu.cn/document/uAjLw4CM/uMzNwEjLzcDMx4yM3ATM/management-practice/app-service-provider-security-management-specifications)
|
|
702
|
+
- [Lark User Terms of Service](https://www.larksuite.com/user-terms-of-service)
|
|
703
|
+
- [Lark Privacy Policy](https://www.larksuite.com/privacy-policy)
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Third-Party Notices
|
|
2
|
+
|
|
3
|
+
This product — the `meegle` CLI distributed via the `@lark-project/meegle`
|
|
4
|
+
npm package and the standalone Go binaries under `dist/` — includes third-party
|
|
5
|
+
open-source software. Each component is listed below with its project URL,
|
|
6
|
+
license type, copyright holder, and the path to the full license text shipped
|
|
7
|
+
in the `third_party_licenses/` directory.
|
|
8
|
+
|
|
9
|
+
If you redistribute this product, you must retain this file and the
|
|
10
|
+
`third_party_licenses/` directory.
|
|
11
|
+
|
|
12
|
+
Inventory was generated with `go-licenses report ./cmd/meegle` against the
|
|
13
|
+
module graph pinned by `go.mod` / `go.sum`.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Components
|
|
18
|
+
|
|
19
|
+
### Direct dependencies
|
|
20
|
+
|
|
21
|
+
| Component | Version | License | Copyright | License file |
|
|
22
|
+
|---|---|---|---|---|
|
|
23
|
+
| [github.com/spf13/cobra](https://github.com/spf13/cobra) | v1.9.1 | Apache-2.0 | The Cobra Authors | [third_party_licenses/github.com/spf13/cobra/LICENSE.txt](third_party_licenses/github.com/spf13/cobra/LICENSE.txt) |
|
|
24
|
+
| [github.com/spf13/pflag](https://github.com/spf13/pflag) | v1.0.6 | BSD-3-Clause | (c) 2012 Alex Ogier; (c) 2012 The Go Authors | [third_party_licenses/github.com/spf13/pflag/LICENSE](third_party_licenses/github.com/spf13/pflag/LICENSE) |
|
|
25
|
+
| [github.com/olekukonko/tablewriter](https://github.com/olekukonko/tablewriter) | v1.1.4 | MIT | (C) 2014 Oleku Konko | [third_party_licenses/github.com/olekukonko/tablewriter/LICENSE.md](third_party_licenses/github.com/olekukonko/tablewriter/LICENSE.md) |
|
|
26
|
+
| [github.com/mdp/qrterminal/v3](https://github.com/mdp/qrterminal) | v3.2.1 | MIT | (c) 2019 Mark Percival | [third_party_licenses/github.com/mdp/qrterminal/v3/LICENSE](third_party_licenses/github.com/mdp/qrterminal/v3/LICENSE) |
|
|
27
|
+
| [github.com/pkg/browser](https://github.com/pkg/browser) | v0.0.0-20240102092130-5ac0b6a4141c | BSD-2-Clause | (c) 2014 Dave Cheney | [third_party_licenses/github.com/pkg/browser/LICENSE](third_party_licenses/github.com/pkg/browser/LICENSE) |
|
|
28
|
+
| [golang.org/x/crypto](https://cs.opensource.google/go/x/crypto) | v0.33.0 | BSD-3-Clause | (c) 2009 The Go Authors | [third_party_licenses/golang.org/x/crypto/pbkdf2/LICENSE](third_party_licenses/golang.org/x/crypto/pbkdf2/LICENSE) |
|
|
29
|
+
| [golang.org/x/sys](https://cs.opensource.google/go/x/sys) | v0.30.0 | BSD-3-Clause | (c) 2009 The Go Authors | [third_party_licenses/golang.org/x/sys/unix/LICENSE](third_party_licenses/golang.org/x/sys/unix/LICENSE) |
|
|
30
|
+
| [golang.org/x/term](https://cs.opensource.google/go/x/term) | v0.29.0 | BSD-3-Clause | (c) 2009 The Go Authors | [third_party_licenses/golang.org/x/term/LICENSE](third_party_licenses/golang.org/x/term/LICENSE) |
|
|
31
|
+
| [gopkg.in/yaml.v3](https://github.com/go-yaml/yaml) | v3.0.1 | MIT + Apache-2.0 | (c) 2011-2016 Canonical Ltd; (c) 2006-2011 Kirill Simonov | [third_party_licenses/gopkg.in/yaml.v3/LICENSE](third_party_licenses/gopkg.in/yaml.v3/LICENSE) · [NOTICE](third_party_licenses/gopkg.in/yaml.v3/NOTICE) |
|
|
32
|
+
|
|
33
|
+
### Transitive dependencies
|
|
34
|
+
|
|
35
|
+
| Component | Version | License | Copyright | License file |
|
|
36
|
+
|---|---|---|---|---|
|
|
37
|
+
| [github.com/cespare/xxhash/v2](https://github.com/cespare/xxhash) | v2.3.0 | MIT | (c) 2016 Caleb Spare | [third_party_licenses/github.com/cespare/xxhash/v2/LICENSE.txt](third_party_licenses/github.com/cespare/xxhash/v2/LICENSE.txt) |
|
|
38
|
+
| [github.com/clipperhouse/displaywidth](https://github.com/clipperhouse/displaywidth) | v0.10.0 | MIT | (c) 2025 Matt Sherman | [third_party_licenses/github.com/clipperhouse/displaywidth/LICENSE](third_party_licenses/github.com/clipperhouse/displaywidth/LICENSE) |
|
|
39
|
+
| [github.com/clipperhouse/uax29/v2](https://github.com/clipperhouse/uax29) | v2.6.0 | MIT | (c) 2020 Matt Sherman | [third_party_licenses/github.com/clipperhouse/uax29/v2/graphemes/LICENSE](third_party_licenses/github.com/clipperhouse/uax29/v2/graphemes/LICENSE) |
|
|
40
|
+
| [github.com/fatih/color](https://github.com/fatih/color) | v1.18.0 | MIT | (c) 2013 Fatih Arslan | [third_party_licenses/github.com/fatih/color/LICENSE.md](third_party_licenses/github.com/fatih/color/LICENSE.md) |
|
|
41
|
+
| [github.com/mattn/go-colorable](https://github.com/mattn/go-colorable) | v0.1.14 | MIT | (c) 2016 Yasuhiro Matsumoto | [third_party_licenses/github.com/mattn/go-colorable/LICENSE](third_party_licenses/github.com/mattn/go-colorable/LICENSE) |
|
|
42
|
+
| [github.com/mattn/go-isatty](https://github.com/mattn/go-isatty) | v0.0.20 | MIT | (c) Yasuhiro Matsumoto | [third_party_licenses/github.com/mattn/go-isatty/LICENSE](third_party_licenses/github.com/mattn/go-isatty/LICENSE) |
|
|
43
|
+
| [github.com/mattn/go-runewidth](https://github.com/mattn/go-runewidth) | v0.0.19 | MIT | (c) 2016 Yasuhiro Matsumoto | [third_party_licenses/github.com/mattn/go-runewidth/LICENSE](third_party_licenses/github.com/mattn/go-runewidth/LICENSE) |
|
|
44
|
+
| [github.com/olekukonko/cat](https://github.com/olekukonko/cat) | v0.0.0-20250911104152-50322a0618f6 | MIT | (c) 2025 Oleku Konko | [third_party_licenses/github.com/olekukonko/cat/LICENSE](third_party_licenses/github.com/olekukonko/cat/LICENSE) |
|
|
45
|
+
| [github.com/olekukonko/errors](https://github.com/olekukonko/errors) | v1.2.0 | MIT | (c) 2025 Oleku Konko | [third_party_licenses/github.com/olekukonko/errors/LICENSE](third_party_licenses/github.com/olekukonko/errors/LICENSE) |
|
|
46
|
+
| [github.com/olekukonko/ll](https://github.com/olekukonko/ll) | v0.1.6 | MIT | (c) 2025 Oleku Konko | [third_party_licenses/github.com/olekukonko/ll/LICENSE](third_party_licenses/github.com/olekukonko/ll/LICENSE) |
|
|
47
|
+
| [rsc.io/qr](https://github.com/rsc/qr) | v0.2.0 | BSD-3-Clause | (c) 2009 The Go Authors | [third_party_licenses/rsc.io/qr/LICENSE](third_party_licenses/rsc.io/qr/LICENSE) |
|
|
48
|
+
|
|
49
|
+
> `github.com/inconshreveable/mousetrap` v1.1.0 (Apache-2.0) is a transitive
|
|
50
|
+
> dependency of `cobra` that is only compiled into the Windows binary. Its
|
|
51
|
+
> license text is bundled with the `win32-*` distributions when applicable.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Go standard library
|
|
56
|
+
|
|
57
|
+
The Go standard library, runtime, and toolchain-provided packages are
|
|
58
|
+
distributed under the BSD-3-Clause license (© The Go Authors). See
|
|
59
|
+
<https://go.dev/LICENSE>. These are not enumerated individually above.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## How to refresh this file
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
go install github.com/google/go-licenses@latest
|
|
67
|
+
|
|
68
|
+
# Inventory (CSV)
|
|
69
|
+
go-licenses report ./cmd/meegle
|
|
70
|
+
|
|
71
|
+
# Full license texts on disk
|
|
72
|
+
go-licenses save ./cmd/meegle --save_path=./third_party_licenses --force
|
|
73
|
+
|
|
74
|
+
# Supply-chain safety gate
|
|
75
|
+
go-licenses check ./cmd/meegle --disallowed_types=forbidden,restricted
|
|
76
|
+
```
|
package/bin/meegle-darwin-arm64
CHANGED
|
Binary file
|
package/bin/meegle-darwin-x64
CHANGED
|
Binary file
|
package/bin/meegle-linux-arm64
CHANGED
|
Binary file
|
package/bin/meegle-linux-x64
CHANGED
|
Binary file
|
|
Binary file
|
package/bin/meegle-win32-x64.exe
CHANGED
|
Binary file
|
package/bin/meegle.js
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
const { spawnSync } = require("child_process");
|
|
3
|
+
const fs = require("fs");
|
|
3
4
|
const os = require("os");
|
|
4
5
|
const path = require("path");
|
|
5
6
|
|
|
7
|
+
const SUPPORTED = [
|
|
8
|
+
"darwin-arm64", "darwin-x64",
|
|
9
|
+
"linux-arm64", "linux-x64",
|
|
10
|
+
"win32-arm64", "win32-x64",
|
|
11
|
+
];
|
|
12
|
+
|
|
6
13
|
const platform = os.platform();
|
|
7
14
|
const arch = os.arch();
|
|
8
15
|
const ext = platform === "win32" ? ".exe" : "";
|
|
@@ -10,15 +17,28 @@ const binName = `meegle-${platform}-${arch}${ext}`;
|
|
|
10
17
|
const binPath = path.join(__dirname, binName);
|
|
11
18
|
|
|
12
19
|
try {
|
|
13
|
-
|
|
20
|
+
fs.accessSync(binPath, fs.constants.X_OK);
|
|
14
21
|
} catch {
|
|
22
|
+
const detected = `${platform}-${arch}`;
|
|
23
|
+
const isSupported = SUPPORTED.includes(detected);
|
|
15
24
|
console.error(
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
25
|
+
isSupported
|
|
26
|
+
? `meegle binary is missing or not executable.\n` +
|
|
27
|
+
`Detected platform: ${detected}\n` +
|
|
28
|
+
`Expected binary at: ${binPath}\n` +
|
|
29
|
+
`Try reinstalling: npm i -g @lark-project/meegle\n` +
|
|
30
|
+
`If the problem persists, please file an issue with the output of: node -v && npm -v`
|
|
31
|
+
: `Unsupported platform: ${detected}\n` +
|
|
32
|
+
`Supported platforms: ${SUPPORTED.join(", ")}`
|
|
19
33
|
);
|
|
20
34
|
process.exit(1);
|
|
21
35
|
}
|
|
22
36
|
|
|
23
37
|
const result = spawnSync(binPath, process.argv.slice(2), { stdio: "inherit" });
|
|
38
|
+
|
|
39
|
+
// Re-raise the signal so parent shells see the real cause (e.g. 130 for SIGINT)
|
|
40
|
+
// instead of a generic exit 1.
|
|
41
|
+
if (result.signal) {
|
|
42
|
+
process.kill(process.pid, result.signal);
|
|
43
|
+
}
|
|
24
44
|
process.exit(result.status ?? 1);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lark-project/meegle",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Agent-First CLI for Meegle (Lark Project)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
|
@@ -23,13 +23,13 @@
|
|
|
23
23
|
"bin/meegle-*",
|
|
24
24
|
"README.md",
|
|
25
25
|
"README.zh-CN.md",
|
|
26
|
-
"
|
|
26
|
+
"CHANGELOG.md",
|
|
27
|
+
"LICENSE",
|
|
28
|
+
"THIRD_PARTY_NOTICES.md",
|
|
29
|
+
"third_party_licenses/**"
|
|
27
30
|
],
|
|
28
|
-
"publishConfig": {
|
|
29
|
-
"tag": "beta"
|
|
30
|
-
},
|
|
31
31
|
"scripts": {
|
|
32
|
-
"prepublishOnly": "cp ../../README.zh-CN.md ./README.zh-CN.md && cp ../../LICENSE ./LICENSE && cp ../../README.md ./README.md"
|
|
32
|
+
"prepublishOnly": "cp ../../README.zh-CN.md ./README.zh-CN.md && cp ../../LICENSE ./LICENSE && cp ../../CHANGELOG.md ./CHANGELOG.md && cp ../../THIRD_PARTY_NOTICES.md ./THIRD_PARTY_NOTICES.md && rm -rf ./third_party_licenses && cp -R ../../third_party_licenses ./third_party_licenses && cp ../../README.md ./README.md"
|
|
33
33
|
},
|
|
34
34
|
"keywords": [
|
|
35
35
|
"meegle",
|