@lark-project/meegle 1.0.9 → 1.0.11
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 +19 -53
- package/README.zh-CN.md +19 -51
- package/bin/install-wizard.js +389 -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 +31 -23
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,23 @@ versioned section on each npm release.
|
|
|
8
8
|
|
|
9
9
|
## [Unreleased]
|
|
10
10
|
|
|
11
|
+
## [v1.0.11] - 2026-06-26
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- `meegle install` now runs a one-stop setup wizard for npm users: install or upgrade the CLI globally, install the AI Agent Skill, configure the host, and start browser or Device Code login. This enables `npx @lark-project/meegle@latest install` as the primary quick-start command.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- The install wizard now invokes the `skills` CLI through `npm exec --package=skills` so `skills add` and `skills ls` are parsed reliably across npm/npx versions. A real isolated install smoke test previously exposed that `npx -y skills add ...` could be misparsed as `add@latest`.
|
|
20
|
+
|
|
21
|
+
## [v1.0.10] - 2026-06-25
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- `mywork todo` now adds an actionable troubleshooting suggestion when the backend returns `get action info fail`, pointing users to refresh command metadata and pass `--asset-key` for multi-workspace accounts.
|
|
26
|
+
- Meegle runtime flags such as `--refresh` and `--profile` are no longer forwarded as backend tool parameters when passed explicitly on dynamic commands.
|
|
27
|
+
|
|
11
28
|
## [v1.0.9] - 2026-06-11
|
|
12
29
|
|
|
13
30
|
### Added
|
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ Command-line tool for [Meegle](https://meegle.com?utm_source=github&utm_medium=r
|
|
|
12
12
|
|
|
13
13
|
## Why Meegle CLI?
|
|
14
14
|
|
|
15
|
-
- **Agent-Native** —
|
|
15
|
+
- **Agent-Native** — The setup wizard installs the bundled AI Agent Skill for Trae, Claude Code, Cursor, Windsurf, Gemini CLI and other agents. Every CLI command is designed for both humans and agents, with structured JSON output, `--dry-run` previews, and `--device-code` flows for non-TTY environments
|
|
16
16
|
- **Broad Coverage** — 16 business domains (work items, workflow, subtasks, comments, work hours, relations, my-work, views, charts, team, user, project, attachments, deliverables, resource library, WBS plan tables) and 50+ commands mapping to Meegle's core capabilities
|
|
17
17
|
- **Two-Layer Parameters** — Ergonomic `--flag-name` for everyday use, fallback `--params <json>` for complex payloads like `fields[]` — pick the right granularity per call
|
|
18
18
|
- **Flexible Output** — `json` / `table` / `ndjson` / `raw`, with `--select` dot-path projection for piping to other tools
|
|
@@ -47,22 +47,21 @@ Command-line tool for [Meegle](https://meegle.com?utm_source=github&utm_medium=r
|
|
|
47
47
|
|
|
48
48
|
- Node.js >= 16 (ships with `npm` / `npx`)
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
Run the setup wizard:
|
|
51
51
|
|
|
52
52
|
```bash
|
|
53
|
-
|
|
53
|
+
npx @lark-project/meegle@latest install
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
+
The wizard installs or upgrades the CLI globally, installs the AI Agent Skill, configures the Meegle host, and starts login.
|
|
57
|
+
|
|
56
58
|
## Quick Start (Human Users)
|
|
57
59
|
|
|
58
|
-
> **Note for AI assistants:** if you are an AI Agent helping the user set this up, jump directly to [Quick Start (AI Agent)](#quick-start-ai-agent--ci--headless) — it contains the non-interactive
|
|
60
|
+
> **Note for AI assistants:** if you are an AI Agent helping the user set this up, jump directly to [Quick Start (AI Agent)](#quick-start-ai-agent--ci--headless) — it contains the non-interactive command you need.
|
|
59
61
|
|
|
60
62
|
```bash
|
|
61
|
-
#
|
|
62
|
-
meegle
|
|
63
|
-
|
|
64
|
-
# 1. Log in (arrow-key host picker + browser OAuth)
|
|
65
|
-
meegle auth login
|
|
63
|
+
# 1. Install CLI + Skill, configure host, and log in
|
|
64
|
+
npx @lark-project/meegle@latest install
|
|
66
65
|
|
|
67
66
|
# 2. View this week's to-dos
|
|
68
67
|
meegle mywork todo --action this_week --page-num 1
|
|
@@ -77,37 +76,15 @@ meegle inspect workitem.create
|
|
|
77
76
|
|
|
78
77
|
## Quick Start (AI Agent / CI / Headless)
|
|
79
78
|
|
|
80
|
-
The default
|
|
81
|
-
|
|
82
|
-
**Step 1 — Install the CLI**
|
|
83
|
-
|
|
84
|
-
```bash
|
|
85
|
-
npm install -g @lark-project/meegle
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
**Step 2 — Persist the host**
|
|
79
|
+
The default browser OAuth flow requires a real TTY. In CI runners, pipes, and agent shells like Claude Code, run the same setup wizard with an explicit host and Device Code login:
|
|
89
80
|
|
|
90
81
|
```bash
|
|
91
|
-
meegle
|
|
82
|
+
npx -y @lark-project/meegle@latest install --host <host> --device-code --lang en
|
|
92
83
|
```
|
|
93
84
|
|
|
94
|
-
Examples of `<host>`: `project.feishu.cn`, `meegle.com`, or your self-hosted tenant domain such as `your-tenant.example.com`.
|
|
95
|
-
|
|
96
|
-
**Step 3 — Log in with Device Code**
|
|
97
|
-
|
|
98
|
-
> Run this command in the background. It prints an authorization URL — extract it and send it to the user. The command exits automatically once the user completes authorization in the browser.
|
|
99
|
-
|
|
100
|
-
```bash
|
|
101
|
-
meegle auth login --device-code
|
|
102
|
-
```
|
|
85
|
+
Examples of `<host>`: `project.feishu.cn`, `meegle.com`, or your self-hosted tenant domain such as `your-tenant.example.com`. The Device Code flow prints an authorization URL; send it to the user and keep the command running until authorization completes.
|
|
103
86
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
```bash
|
|
107
|
-
meegle auth login --device-code --host <host>
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
**Step 4 — Verify**
|
|
87
|
+
Verify:
|
|
111
88
|
|
|
112
89
|
```bash
|
|
113
90
|
meegle auth status
|
|
@@ -117,19 +94,7 @@ For fully unattended CI (no human-in-the-loop), inject a token via environment v
|
|
|
117
94
|
|
|
118
95
|
## AI Agent Skill
|
|
119
96
|
|
|
120
|
-
`skills/meegle
|
|
121
|
-
|
|
122
|
-
### Install
|
|
123
|
-
|
|
124
|
-
```bash
|
|
125
|
-
# Install the CLI first (the skill calls `meegle` under the hood)
|
|
126
|
-
npm install -g @lark-project/meegle
|
|
127
|
-
|
|
128
|
-
# Then add the skill — auto-detects installed agents and registers in each
|
|
129
|
-
npx skills add larksuite/meegle-cli -y -g
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
`npx skills add` reads `skills/meegle/SKILL.md` from the repo and drops it into the skill directory of every agent CLI it finds on the machine. Re-run any time to pick up updates.
|
|
97
|
+
The setup wizard installs `skills/meegle/`, a drop-in skill for Trae, Claude Code, Cursor, Windsurf, Gemini CLI, GitHub Copilot CLI, and other agents. It teaches agents how to operate Meegle through this CLI instead of guessing command shapes from prose.
|
|
133
98
|
|
|
134
99
|
### What it covers
|
|
135
100
|
|
|
@@ -140,11 +105,9 @@ npx skills add larksuite/meegle-cli -y -g
|
|
|
140
105
|
- **SOPs** — step-by-step playbooks for creating work items, transitioning nodes, transitioning states, and updating fields
|
|
141
106
|
- **Auth guard** — the skill refuses to run business commands until `meegle auth status` succeeds
|
|
142
107
|
|
|
143
|
-
See [skills/meegle/SKILL.md](./skills/meegle/SKILL.md) and [skills/meegle/references/](./skills/meegle/references/) for the full contents.
|
|
144
|
-
|
|
145
108
|
### Usage
|
|
146
109
|
|
|
147
|
-
Once
|
|
110
|
+
Once the setup wizard has run, ask the agent in natural language. For example:
|
|
148
111
|
|
|
149
112
|
```
|
|
150
113
|
Show me this week's P0 stories in the PROJ space.
|
|
@@ -152,8 +115,6 @@ Show me this week's P0 stories in the PROJ space.
|
|
|
152
115
|
|
|
153
116
|
The agent consults the skill, picks the right `meegle` commands, and runs them for you. Pair with `--dry-run` (see [Security](#security--risk-warnings)) to preview side-effectful operations before the agent commits them.
|
|
154
117
|
|
|
155
|
-
> Heads up: the skill name `meegle` is the same string as the CLI binary. When documentation refers to "the **`meegle` skill**" it means the files in `skills/meegle/`; when it refers to "the **`meegle` CLI**" it means the `meegle` command on your PATH.
|
|
156
|
-
|
|
157
118
|
## Commands
|
|
158
119
|
|
|
159
120
|
### workitem — Work Items
|
|
@@ -332,6 +293,11 @@ meegle mywork todo --action done --page-num 1
|
|
|
332
293
|
meegle mywork todo --action overdue --page-num 1
|
|
333
294
|
```
|
|
334
295
|
|
|
296
|
+
If `mywork todo` fails with `get action info fail`, refresh command metadata first:
|
|
297
|
+
`meegle --refresh mywork todo --action this_week --page-num 1`. If your account
|
|
298
|
+
belongs to multiple workspaces, pass the workspace key explicitly:
|
|
299
|
+
`meegle mywork todo --action this_week --page-num 1 --asset-key Asset_xxx`.
|
|
300
|
+
|
|
335
301
|
### Querying Work Items
|
|
336
302
|
|
|
337
303
|
```bash
|
package/README.zh-CN.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
## 为什么选择 Meegle CLI?
|
|
14
14
|
|
|
15
|
-
- **Agent 友好** —
|
|
15
|
+
- **Agent 友好** — 安装向导会安装内置 AI Agent Skill,把 Meegle 操作手册喂给 Trae、Claude Code、Cursor、Windsurf、Gemini CLI 等主流 Agent。CLI 命令同时面向人类和 Agent 设计,结构化 JSON 输出、`--dry-run` 预览、`--device-code` 无 TTY 流程
|
|
16
16
|
- **覆盖完整** — 16 个业务域(工作项、工作流、子任务、评论、工时、关联、我的工作、视图、图表、团队、用户、空间、附件、交付物、资源库、WBS 计划表),50+ 命令映射到 Meegle 核心能力
|
|
17
17
|
- **两层参数模型** — 日常用 `--flag-name` 轻便直接,复杂载荷(如 `fields[]`)用 `--params <json>` 兜底 —— 按场景选择合适粒度
|
|
18
18
|
- **输出格式灵活** — 支持 `json` / `table` / `ndjson` / `raw`,配合 `--select` 点路径投影可直接 pipe 给其他工具
|
|
@@ -47,22 +47,21 @@
|
|
|
47
47
|
|
|
48
48
|
- Node.js >= 16(自带 `npm` / `npx`)
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
运行安装向导:
|
|
51
51
|
|
|
52
52
|
```bash
|
|
53
|
-
|
|
53
|
+
npx @lark-project/meegle@latest install
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
+
向导会全局安装或升级 CLI、安装 AI Agent Skill、配置 Meegle host,并启动登录。
|
|
57
|
+
|
|
56
58
|
## 快速开始(人类用户)
|
|
57
59
|
|
|
58
|
-
> **给 AI Agent 的提示:** 如果你是在替用户完成这套安装的 AI Agent,请直接跳到 [快速开始(AI Agent)](#快速开始ai-agent--ci--无头环境) ——
|
|
60
|
+
> **给 AI Agent 的提示:** 如果你是在替用户完成这套安装的 AI Agent,请直接跳到 [快速开始(AI Agent)](#快速开始ai-agent--ci--无头环境) —— 那里有你需要的非交互命令。
|
|
59
61
|
|
|
60
62
|
```bash
|
|
61
|
-
#
|
|
62
|
-
meegle
|
|
63
|
-
|
|
64
|
-
# 1. 登录(上下选择 host + 浏览器 OAuth)
|
|
65
|
-
meegle auth login
|
|
63
|
+
# 1. 安装 CLI + Skill,配置 host,并登录
|
|
64
|
+
npx @lark-project/meegle@latest install
|
|
66
65
|
|
|
67
66
|
# 2. 查看本周待办
|
|
68
67
|
meegle mywork todo --action this_week --page-num 1
|
|
@@ -77,37 +76,17 @@ meegle inspect workitem.create
|
|
|
77
76
|
|
|
78
77
|
## 快速开始(AI Agent / CI / 无头环境)
|
|
79
78
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
**Step 1 — 安装 CLI**
|
|
79
|
+
默认浏览器 OAuth 流程依赖真实 TTY。在 CI Runner、管道、Claude Code 这类环境里,使用同一个安装向导,但显式传入 host 并启用 Device Code 登录:
|
|
83
80
|
|
|
84
81
|
```bash
|
|
85
|
-
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
**Step 2 — 持久化 host**
|
|
89
|
-
|
|
90
|
-
```bash
|
|
91
|
-
meegle config set host <host>
|
|
82
|
+
npx -y @lark-project/meegle@latest install --host <host> --device-code --lang zh
|
|
92
83
|
```
|
|
93
84
|
|
|
94
85
|
`<host>` 示例:`project.feishu.cn`、`meegle.com`,或自建租户域名(如 `your-tenant.example.com`)。
|
|
95
86
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
> 建议后台执行。命令会输出授权 URL —— 提取后发给用户,用户在浏览器完成授权后命令自动退出。
|
|
99
|
-
|
|
100
|
-
```bash
|
|
101
|
-
meegle auth login --device-code
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
如不想持久化 host,也可以每次显式传入:
|
|
105
|
-
|
|
106
|
-
```bash
|
|
107
|
-
meegle auth login --device-code --host <host>
|
|
108
|
-
```
|
|
87
|
+
Device Code 流程会输出授权 URL;把链接发给用户,并保持命令运行直到用户在浏览器完成授权。
|
|
109
88
|
|
|
110
|
-
|
|
89
|
+
验证:
|
|
111
90
|
|
|
112
91
|
```bash
|
|
113
92
|
meegle auth status
|
|
@@ -117,19 +96,7 @@ meegle auth status
|
|
|
117
96
|
|
|
118
97
|
## AI Agent Skill
|
|
119
98
|
|
|
120
|
-
`skills/meegle
|
|
121
|
-
|
|
122
|
-
### 安装
|
|
123
|
-
|
|
124
|
-
```bash
|
|
125
|
-
# 先装 CLI(skill 底层会调用 meegle 命令)
|
|
126
|
-
npm install -g @lark-project/meegle
|
|
127
|
-
|
|
128
|
-
# 再加载 skill —— 会自动探测已装的 Agent,并在各自的 skill 目录里登记
|
|
129
|
-
npx skills add larksuite/meegle-cli -y -g
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
`npx skills add` 会从仓库读取 `skills/meegle/SKILL.md`,写入本机所有 Agent CLI 的 skill 目录。每次升级重跑一遍即可拉取最新内容。
|
|
99
|
+
安装向导会安装 `skills/meegle/`,这是一份可直接加载到 Trae、Claude Code、Cursor、Windsurf、Gemini CLI、GitHub Copilot CLI 等 Agent 的 skill。它会教 Agent 通过本 CLI 操作 Meegle,而不是从自然语言说明里猜命令形状。
|
|
133
100
|
|
|
134
101
|
### 覆盖内容
|
|
135
102
|
|
|
@@ -140,11 +107,9 @@ npx skills add larksuite/meegle-cli -y -g
|
|
|
140
107
|
- **SOP** — 创建工作项、节点流转、状态流转、更新字段等场景的分步剧本
|
|
141
108
|
- **授权守护** — 所有业务命令前强制先过 `meegle auth status`
|
|
142
109
|
|
|
143
|
-
完整内容见 [skills/meegle/SKILL.md](./skills/meegle/SKILL.md) 和 [skills/meegle/references/](./skills/meegle/references/)。
|
|
144
|
-
|
|
145
110
|
### 使用方式
|
|
146
111
|
|
|
147
|
-
|
|
112
|
+
安装向导执行完成后,直接用自然语言和 Agent 对话。例如:
|
|
148
113
|
|
|
149
114
|
```
|
|
150
115
|
帮我看一下 PROJ 空间本周待办里的 P0 需求
|
|
@@ -152,8 +117,6 @@ npx skills add larksuite/meegle-cli -y -g
|
|
|
152
117
|
|
|
153
118
|
Agent 会参考 skill,自动选择合适的 `meegle` 命令执行。配合 `--dry-run`(见 [安全](#安全与风险提示))可以在 Agent 真正执行副作用前先预览请求。
|
|
154
119
|
|
|
155
|
-
> 小提示:skill 的名字 `meegle` 和 CLI 二进制同名。文档里提到 **`meegle` skill** 时指 `skills/meegle/` 里的文件;提到 **`meegle` CLI** 时指你 PATH 上的 `meegle` 命令。
|
|
156
|
-
|
|
157
120
|
## 命令一览
|
|
158
121
|
|
|
159
122
|
### workitem — 工作项域
|
|
@@ -332,6 +295,11 @@ meegle mywork todo --action done --page-num 1
|
|
|
332
295
|
meegle mywork todo --action overdue --page-num 1
|
|
333
296
|
```
|
|
334
297
|
|
|
298
|
+
如果 `mywork todo` 返回 `get action info fail`,先刷新命令元数据:
|
|
299
|
+
`meegle --refresh mywork todo --action this_week --page-num 1`。如果账号同时属于多个工作区,
|
|
300
|
+
请显式传入工作区 key:
|
|
301
|
+
`meegle mywork todo --action this_week --page-num 1 --asset-key Asset_xxx`。
|
|
302
|
+
|
|
335
303
|
### 查询工作项
|
|
336
304
|
|
|
337
305
|
```bash
|
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { execFileSync, spawnSync } = require("child_process");
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const os = require("os");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
const readline = require("readline");
|
|
7
|
+
|
|
8
|
+
const PKG = "@lark-project/meegle";
|
|
9
|
+
const SKILLS_REPO = "larksuite/meegle-cli";
|
|
10
|
+
const DEFAULT_HOST = "meegle.com";
|
|
11
|
+
const PRESET_HOSTS = [
|
|
12
|
+
{ label: "Feishu Project (project.feishu.cn)", value: "project.feishu.cn" },
|
|
13
|
+
{ label: "Meegle (meegle.com)", value: "meegle.com" },
|
|
14
|
+
];
|
|
15
|
+
const isWindows = process.platform === "win32";
|
|
16
|
+
|
|
17
|
+
const messages = {
|
|
18
|
+
en: {
|
|
19
|
+
setup: "Setting up Meegle CLI...",
|
|
20
|
+
language: "Select language",
|
|
21
|
+
installing: "Installing %s globally...",
|
|
22
|
+
upgrading: "Upgrading %s (v%s -> v%s)...",
|
|
23
|
+
installedSkip: "Already installed globally (v%s). Skipped",
|
|
24
|
+
installed: "CLI installed globally",
|
|
25
|
+
skills: "Installing AI Agent Skill...",
|
|
26
|
+
skillsSkip: "AI Agent Skill already appears to be installed. Skipped",
|
|
27
|
+
skillsDone: "AI Agent Skill installed",
|
|
28
|
+
hostExisting: "Found existing host: %s. Use it?",
|
|
29
|
+
hostSelect: "Select Meegle site",
|
|
30
|
+
hostCustom: "Custom domain",
|
|
31
|
+
hostInput: "Enter custom domain, for example project.feishu.cn: ",
|
|
32
|
+
hostConfigured: "Host configured: %s",
|
|
33
|
+
authSkip: "Skipped authorization. Run later: meegle auth login",
|
|
34
|
+
authAlready: "Already authenticated. Skipped authorization",
|
|
35
|
+
authConfirm: "Log in now?",
|
|
36
|
+
authBrowser: "Starting browser OAuth login...",
|
|
37
|
+
authDevice: "Starting Device Code login...",
|
|
38
|
+
done: "Setup complete. Try: meegle mywork todo --action this_week --page-num 1",
|
|
39
|
+
nonTtyHost: "No TTY detected and no --host was provided. Using default host: %s",
|
|
40
|
+
},
|
|
41
|
+
zh: {
|
|
42
|
+
setup: "正在设置 Meegle CLI...",
|
|
43
|
+
language: "请选择语言",
|
|
44
|
+
installing: "正在全局安装 %s...",
|
|
45
|
+
upgrading: "正在升级 %s (v%s -> v%s)...",
|
|
46
|
+
installedSkip: "已全局安装 (v%s),跳过",
|
|
47
|
+
installed: "CLI 已全局安装",
|
|
48
|
+
skills: "正在安装 AI Agent Skill...",
|
|
49
|
+
skillsSkip: "检测到 AI Agent Skill 可能已安装,跳过",
|
|
50
|
+
skillsDone: "AI Agent Skill 已安装",
|
|
51
|
+
hostExisting: "发现已有 host: %s,继续使用?",
|
|
52
|
+
hostSelect: "请选择 Meegle 站点",
|
|
53
|
+
hostCustom: "自定义域名",
|
|
54
|
+
hostInput: "请输入自定义域名,例如 project.feishu.cn: ",
|
|
55
|
+
hostConfigured: "Host 已配置: %s",
|
|
56
|
+
authSkip: "已跳过授权。后续可运行: meegle auth login",
|
|
57
|
+
authAlready: "已登录,跳过授权",
|
|
58
|
+
authConfirm: "现在登录?",
|
|
59
|
+
authBrowser: "正在启动浏览器 OAuth 登录...",
|
|
60
|
+
authDevice: "正在启动 Device Code 登录...",
|
|
61
|
+
done: "安装完成。可以试试: meegle mywork todo --action this_week --page-num 1",
|
|
62
|
+
nonTtyHost: "当前不是交互终端且未传 --host,将使用默认 host: %s",
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
function fmt(template, ...values) {
|
|
67
|
+
let i = 0;
|
|
68
|
+
return template.replace(/%s/g, () => String(values[i++] ?? ""));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function parseArgs(argv) {
|
|
72
|
+
const opts = {
|
|
73
|
+
auth: true,
|
|
74
|
+
skills: true,
|
|
75
|
+
deviceCode: false,
|
|
76
|
+
yes: false,
|
|
77
|
+
host: "",
|
|
78
|
+
lang: "",
|
|
79
|
+
};
|
|
80
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
81
|
+
const arg = argv[i];
|
|
82
|
+
if (arg === "--host" && argv[i + 1]) {
|
|
83
|
+
opts.host = argv[++i];
|
|
84
|
+
} else if (arg.startsWith("--host=")) {
|
|
85
|
+
opts.host = arg.slice("--host=".length);
|
|
86
|
+
} else if (arg === "--lang" && argv[i + 1]) {
|
|
87
|
+
opts.lang = argv[++i];
|
|
88
|
+
} else if (arg.startsWith("--lang=")) {
|
|
89
|
+
opts.lang = arg.slice("--lang=".length);
|
|
90
|
+
} else if (arg === "--device-code") {
|
|
91
|
+
opts.deviceCode = true;
|
|
92
|
+
} else if (arg === "--no-auth") {
|
|
93
|
+
opts.auth = false;
|
|
94
|
+
} else if (arg === "--no-skills") {
|
|
95
|
+
opts.skills = false;
|
|
96
|
+
} else if (arg === "-y" || arg === "--yes") {
|
|
97
|
+
opts.yes = true;
|
|
98
|
+
} else if (arg === "-h" || arg === "--help") {
|
|
99
|
+
printHelp();
|
|
100
|
+
process.exit(0);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (opts.lang !== "zh" && opts.lang !== "en") {
|
|
104
|
+
opts.lang = "";
|
|
105
|
+
}
|
|
106
|
+
return opts;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function printHelp() {
|
|
110
|
+
console.log(`Usage: meegle install [options]
|
|
111
|
+
|
|
112
|
+
Set up Meegle CLI, AI Agent Skill, host configuration, and login.
|
|
113
|
+
|
|
114
|
+
Options:
|
|
115
|
+
--host <host> Configure a site host, e.g. project.feishu.cn
|
|
116
|
+
--device-code Use Device Code login instead of browser OAuth
|
|
117
|
+
--no-auth Skip login
|
|
118
|
+
--no-skills Skip AI Agent Skill installation
|
|
119
|
+
--lang <en|zh> Select wizard language
|
|
120
|
+
-y, --yes Use defaults for non-essential prompts
|
|
121
|
+
-h, --help Show help`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function cmdParts(cmd, args) {
|
|
125
|
+
if (!isWindows) return { cmd, args };
|
|
126
|
+
return { cmd: "cmd.exe", args: ["/c", cmd, ...args] };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function run(cmd, args, opts = {}) {
|
|
130
|
+
const actual = cmdParts(cmd, args);
|
|
131
|
+
const result = spawnSync(actual.cmd, actual.args, {
|
|
132
|
+
stdio: "inherit",
|
|
133
|
+
env: process.env,
|
|
134
|
+
...opts,
|
|
135
|
+
});
|
|
136
|
+
if (result.signal) process.kill(process.pid, result.signal);
|
|
137
|
+
if (result.status !== 0) {
|
|
138
|
+
throw new Error(`${cmd} ${args.join(" ")} failed with exit code ${result.status}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function runSilent(cmd, args, opts = {}) {
|
|
143
|
+
const actual = cmdParts(cmd, args);
|
|
144
|
+
return execFileSync(actual.cmd, actual.args, {
|
|
145
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
146
|
+
encoding: "utf8",
|
|
147
|
+
env: process.env,
|
|
148
|
+
...opts,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function runCapture(cmd, args, opts = {}) {
|
|
153
|
+
const actual = cmdParts(cmd, args);
|
|
154
|
+
return spawnSync(actual.cmd, actual.args, {
|
|
155
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
156
|
+
encoding: "utf8",
|
|
157
|
+
env: process.env,
|
|
158
|
+
...opts,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function question(prompt) {
|
|
163
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
164
|
+
return new Promise((resolve) => {
|
|
165
|
+
rl.question(prompt, (answer) => {
|
|
166
|
+
rl.close();
|
|
167
|
+
resolve(answer.trim());
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async function select(message, options) {
|
|
173
|
+
console.log(message);
|
|
174
|
+
options.forEach((opt, idx) => {
|
|
175
|
+
console.log(` ${idx + 1}. ${opt.label}`);
|
|
176
|
+
});
|
|
177
|
+
for (;;) {
|
|
178
|
+
const answer = await question("> ");
|
|
179
|
+
const idx = Number(answer);
|
|
180
|
+
if (Number.isInteger(idx) && idx >= 1 && idx <= options.length) {
|
|
181
|
+
return options[idx - 1].value;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async function confirm(message, defaultValue) {
|
|
187
|
+
const suffix = defaultValue ? " [Y/n] " : " [y/N] ";
|
|
188
|
+
const answer = (await question(message + suffix)).toLowerCase();
|
|
189
|
+
if (!answer) return defaultValue;
|
|
190
|
+
return answer === "y" || answer === "yes";
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function semverLessThan(a, b) {
|
|
194
|
+
const pa = String(a).replace(/-.*/, "").split(".").map(Number);
|
|
195
|
+
const pb = String(b).replace(/-.*/, "").split(".").map(Number);
|
|
196
|
+
for (let i = 0; i < 3; i += 1) {
|
|
197
|
+
if ((pa[i] || 0) < (pb[i] || 0)) return true;
|
|
198
|
+
if ((pa[i] || 0) > (pb[i] || 0)) return false;
|
|
199
|
+
}
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function getGloballyInstalledVersion() {
|
|
204
|
+
try {
|
|
205
|
+
const out = runSilent("npm", ["list", "-g", PKG, "--depth=0"], { timeout: 15000 });
|
|
206
|
+
const match = out.match(/@lark-project\/meegle@([^\s]+)/);
|
|
207
|
+
return match ? match[1] : "unknown";
|
|
208
|
+
} catch (_) {
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function getLatestVersion() {
|
|
214
|
+
try {
|
|
215
|
+
const out = runSilent("npm", ["view", PKG, "version"], { timeout: 15000 }).trim();
|
|
216
|
+
return /^\d+\.\d+\.\d+/.test(out) ? out : null;
|
|
217
|
+
} catch (_) {
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function whichMeegle() {
|
|
223
|
+
try {
|
|
224
|
+
const prefix = runSilent("npm", ["prefix", "-g"], { timeout: 15000 }).trim();
|
|
225
|
+
const bin = isWindows ? path.join(prefix, "meegle.cmd") : path.join(prefix, "bin", "meegle");
|
|
226
|
+
if (fs.existsSync(bin)) return bin;
|
|
227
|
+
} catch (_) {
|
|
228
|
+
// fall through
|
|
229
|
+
}
|
|
230
|
+
try {
|
|
231
|
+
const cmd = isWindows ? "where" : "which";
|
|
232
|
+
const out = runSilent(cmd, ["meegle"], { timeout: 15000 });
|
|
233
|
+
return out.split(/\r?\n/).map((s) => s.trim()).find(Boolean) || null;
|
|
234
|
+
} catch (_) {
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function normalizeHost(raw) {
|
|
240
|
+
const trimmed = String(raw || "").trim();
|
|
241
|
+
if (!trimmed) return "";
|
|
242
|
+
try {
|
|
243
|
+
const withScheme = trimmed.includes("://") ? trimmed : `https://${trimmed}`;
|
|
244
|
+
return new URL(withScheme).host;
|
|
245
|
+
} catch (_) {
|
|
246
|
+
return trimmed.replace(/^https?:\/\//, "").replace(/\/.*$/, "");
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
async function selectLanguage(opts) {
|
|
251
|
+
if (opts.lang) return opts.lang;
|
|
252
|
+
const locale = (process.env.LANG || process.env.LANGUAGE || "").toLowerCase();
|
|
253
|
+
if (!process.stdin.isTTY || opts.yes) return locale.includes("zh") ? "zh" : "en";
|
|
254
|
+
return select("Select language / 请选择语言", [
|
|
255
|
+
{ label: "English", value: "en" },
|
|
256
|
+
{ label: "中文", value: "zh" },
|
|
257
|
+
]);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
async function stepInstallGlobally(msg) {
|
|
261
|
+
const installed = getGloballyInstalledVersion();
|
|
262
|
+
const latest = getLatestVersion();
|
|
263
|
+
const needsUpgrade = installed && latest && semverLessThan(installed, latest);
|
|
264
|
+
if (installed && !needsUpgrade) {
|
|
265
|
+
console.log(fmt(msg.installedSkip, installed));
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
console.log(needsUpgrade ? fmt(msg.upgrading, PKG, installed, latest) : fmt(msg.installing, PKG));
|
|
269
|
+
run("npm", ["install", "-g", PKG], { timeout: 120000 });
|
|
270
|
+
console.log(msg.installed);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
async function skillsAlreadyInstalled() {
|
|
274
|
+
try {
|
|
275
|
+
const out = runSilent("npm", ["exec", "--yes", "--package=skills", "--", "skills", "ls", "-g"], { timeout: 120000 });
|
|
276
|
+
return /\bmeegle\b/i.test(out);
|
|
277
|
+
} catch (_) {
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
async function stepInstallSkills(msg) {
|
|
283
|
+
console.log(msg.skills);
|
|
284
|
+
if (await skillsAlreadyInstalled()) {
|
|
285
|
+
console.log(msg.skillsSkip);
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
run("npm", ["exec", "--yes", "--package=skills", "--", "skills", "add", SKILLS_REPO, "-y", "-g"], { timeout: 120000 });
|
|
289
|
+
console.log(msg.skillsDone);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function getConfiguredHost(meegleBin) {
|
|
293
|
+
try {
|
|
294
|
+
const out = runSilent(meegleBin, ["config", "get", "host"], { timeout: 15000 }).trim();
|
|
295
|
+
if (!out || out === "(not set)") return "";
|
|
296
|
+
return normalizeHost(out);
|
|
297
|
+
} catch (_) {
|
|
298
|
+
return "";
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
async function resolveHost(opts, msg, meegleBin) {
|
|
303
|
+
if (opts.host) return normalizeHost(opts.host);
|
|
304
|
+
const existing = getConfiguredHost(meegleBin);
|
|
305
|
+
if (existing) {
|
|
306
|
+
if (!process.stdin.isTTY || opts.yes) return existing;
|
|
307
|
+
if (await confirm(fmt(msg.hostExisting, existing), true)) return existing;
|
|
308
|
+
}
|
|
309
|
+
if (!process.stdin.isTTY || opts.yes) {
|
|
310
|
+
console.log(fmt(msg.nonTtyHost, DEFAULT_HOST));
|
|
311
|
+
return DEFAULT_HOST;
|
|
312
|
+
}
|
|
313
|
+
const selected = await select(msg.hostSelect, [
|
|
314
|
+
...PRESET_HOSTS,
|
|
315
|
+
{ label: msg.hostCustom, value: "__custom__" },
|
|
316
|
+
]);
|
|
317
|
+
if (selected !== "__custom__") return selected;
|
|
318
|
+
return normalizeHost(await question(msg.hostInput));
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function configureHost(meegleBin, host, msg) {
|
|
322
|
+
run(meegleBin, ["config", "set", "host", host], { timeout: 30000 });
|
|
323
|
+
console.log(fmt(msg.hostConfigured, host));
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function isAuthenticated(meegleBin) {
|
|
327
|
+
const result = runCapture(meegleBin, ["auth", "status", "--format", "json"], { timeout: 15000 });
|
|
328
|
+
const text = result.stdout || "";
|
|
329
|
+
try {
|
|
330
|
+
const parsed = JSON.parse(text);
|
|
331
|
+
return !!parsed.authenticated;
|
|
332
|
+
} catch (_) {
|
|
333
|
+
return result.status === 0;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
async function stepAuth(meegleBin, host, opts, msg) {
|
|
338
|
+
if (!opts.auth) {
|
|
339
|
+
console.log(msg.authSkip);
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
if (isAuthenticated(meegleBin)) {
|
|
343
|
+
console.log(msg.authAlready);
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
const useDeviceCode = opts.deviceCode || !process.stdin.isTTY;
|
|
347
|
+
if (!useDeviceCode && !opts.yes) {
|
|
348
|
+
const shouldLogin = await confirm(msg.authConfirm, true);
|
|
349
|
+
if (!shouldLogin) {
|
|
350
|
+
console.log(msg.authSkip);
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
console.log(useDeviceCode ? msg.authDevice : msg.authBrowser);
|
|
355
|
+
const args = ["auth", "login", "--host", host];
|
|
356
|
+
if (useDeviceCode) args.push("--device-code");
|
|
357
|
+
run(meegleBin, args, { timeout: 900000 });
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
async function main(argv = process.argv.slice(2)) {
|
|
361
|
+
const opts = parseArgs(argv);
|
|
362
|
+
const lang = await selectLanguage(opts);
|
|
363
|
+
const msg = messages[lang];
|
|
364
|
+
console.log(msg.setup);
|
|
365
|
+
|
|
366
|
+
await stepInstallGlobally(msg);
|
|
367
|
+
if (opts.skills) {
|
|
368
|
+
await stepInstallSkills(msg);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const meegleBin = whichMeegle();
|
|
372
|
+
if (!meegleBin) {
|
|
373
|
+
throw new Error("meegle was installed but no global executable was found in PATH");
|
|
374
|
+
}
|
|
375
|
+
const host = await resolveHost(opts, msg, meegleBin);
|
|
376
|
+
configureHost(meegleBin, host, msg);
|
|
377
|
+
await stepAuth(meegleBin, host, opts, msg);
|
|
378
|
+
|
|
379
|
+
console.log(msg.done);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (require.main === module) {
|
|
383
|
+
main().catch((err) => {
|
|
384
|
+
console.error(err && err.message ? err.message : err);
|
|
385
|
+
process.exit(1);
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
module.exports = { main };
|
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
|
@@ -15,30 +15,38 @@ const arch = os.arch();
|
|
|
15
15
|
const ext = platform === "win32" ? ".exe" : "";
|
|
16
16
|
const binName = `meegle-${platform}-${arch}${ext}`;
|
|
17
17
|
const binPath = path.join(__dirname, binName);
|
|
18
|
+
const args = process.argv.slice(2);
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
`
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
20
|
+
if (args[0] === "install") {
|
|
21
|
+
Promise.resolve(require("./install-wizard.js").main(args.slice(1))).catch((err) => {
|
|
22
|
+
console.error("Unexpected install error:", err && err.message ? err.message : err);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
});
|
|
25
|
+
} else {
|
|
26
|
+
try {
|
|
27
|
+
fs.accessSync(binPath, fs.constants.X_OK);
|
|
28
|
+
} catch {
|
|
29
|
+
const detected = `${platform}-${arch}`;
|
|
30
|
+
const isSupported = SUPPORTED.includes(detected);
|
|
31
|
+
console.error(
|
|
32
|
+
isSupported
|
|
33
|
+
? `meegle binary is missing or not executable.\n` +
|
|
34
|
+
`Detected platform: ${detected}\n` +
|
|
35
|
+
`Expected binary at: ${binPath}\n` +
|
|
36
|
+
`Try reinstalling: npm i -g @lark-project/meegle\n` +
|
|
37
|
+
`If the problem persists, please file an issue with the output of: node -v && npm -v`
|
|
38
|
+
: `Unsupported platform: ${detected}\n` +
|
|
39
|
+
`Supported platforms: ${SUPPORTED.join(", ")}`
|
|
40
|
+
);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
36
43
|
|
|
37
|
-
const result = spawnSync(binPath,
|
|
44
|
+
const result = spawnSync(binPath, args, { stdio: "inherit" });
|
|
38
45
|
|
|
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
|
-
|
|
46
|
+
// Re-raise the signal so parent shells see the real cause (e.g. 130 for SIGINT)
|
|
47
|
+
// instead of a generic exit 1.
|
|
48
|
+
if (result.signal) {
|
|
49
|
+
process.kill(process.pid, result.signal);
|
|
50
|
+
}
|
|
51
|
+
process.exit(result.status ?? 1);
|
|
43
52
|
}
|
|
44
|
-
process.exit(result.status ?? 1);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lark-project/meegle",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.11",
|
|
4
4
|
"description": "Agent-First CLI for Meegle (Lark Project)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/larksuite/meegle-cli#readme",
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
],
|
|
29
29
|
"files": [
|
|
30
30
|
"bin/meegle.js",
|
|
31
|
+
"bin/install-wizard.js",
|
|
31
32
|
"bin/meegle-*",
|
|
32
33
|
"README.md",
|
|
33
34
|
"README.zh-CN.md",
|