@lark-project/meegle 1.0.10 → 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 +10 -0
- package/README.md +14 -61
- package/README.zh-CN.md +14 -59
- 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,16 @@ 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
|
+
|
|
11
21
|
## [v1.0.10] - 2026-06-25
|
|
12
22
|
|
|
13
23
|
### Fixed
|
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,45 +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 — Install the Agent Skill**
|
|
89
|
-
|
|
90
|
-
> Recommended for AI-agent workflows, optional for direct CLI use. The skill teaches agents how to choose and run `meegle` commands correctly.
|
|
91
|
-
|
|
92
|
-
```bash
|
|
93
|
-
npx skills add larksuite/meegle-cli -y -g
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
**Step 3 — 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:
|
|
97
80
|
|
|
98
81
|
```bash
|
|
99
|
-
meegle
|
|
82
|
+
npx -y @lark-project/meegle@latest install --host <host> --device-code --lang en
|
|
100
83
|
```
|
|
101
84
|
|
|
102
|
-
Examples of `<host>`: `project.feishu.cn`, `meegle.com`, or your self-hosted tenant domain such as `your-tenant.example.com`.
|
|
103
|
-
|
|
104
|
-
**Step 4 — Log in with Device Code**
|
|
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.
|
|
105
86
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
```bash
|
|
109
|
-
meegle auth login --device-code
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
Alternatively, pass the host inline each time without persisting it:
|
|
113
|
-
|
|
114
|
-
```bash
|
|
115
|
-
meegle auth login --device-code --host <host>
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
**Step 5 — Verify**
|
|
87
|
+
Verify:
|
|
119
88
|
|
|
120
89
|
```bash
|
|
121
90
|
meegle auth status
|
|
@@ -125,19 +94,7 @@ For fully unattended CI (no human-in-the-loop), inject a token via environment v
|
|
|
125
94
|
|
|
126
95
|
## AI Agent Skill
|
|
127
96
|
|
|
128
|
-
`skills/meegle
|
|
129
|
-
|
|
130
|
-
### Install
|
|
131
|
-
|
|
132
|
-
```bash
|
|
133
|
-
# Install the CLI first (the skill calls `meegle` under the hood)
|
|
134
|
-
npm install -g @lark-project/meegle
|
|
135
|
-
|
|
136
|
-
# Then add the skill — auto-detects installed agents and registers in each
|
|
137
|
-
npx skills add larksuite/meegle-cli -y -g
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
`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.
|
|
141
98
|
|
|
142
99
|
### What it covers
|
|
143
100
|
|
|
@@ -148,11 +105,9 @@ npx skills add larksuite/meegle-cli -y -g
|
|
|
148
105
|
- **SOPs** — step-by-step playbooks for creating work items, transitioning nodes, transitioning states, and updating fields
|
|
149
106
|
- **Auth guard** — the skill refuses to run business commands until `meegle auth status` succeeds
|
|
150
107
|
|
|
151
|
-
See [skills/meegle/SKILL.md](./skills/meegle/SKILL.md) and [skills/meegle/references/](./skills/meegle/references/) for the full contents.
|
|
152
|
-
|
|
153
108
|
### Usage
|
|
154
109
|
|
|
155
|
-
Once
|
|
110
|
+
Once the setup wizard has run, ask the agent in natural language. For example:
|
|
156
111
|
|
|
157
112
|
```
|
|
158
113
|
Show me this week's P0 stories in the PROJ space.
|
|
@@ -160,8 +115,6 @@ Show me this week's P0 stories in the PROJ space.
|
|
|
160
115
|
|
|
161
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.
|
|
162
117
|
|
|
163
|
-
> 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.
|
|
164
|
-
|
|
165
118
|
## Commands
|
|
166
119
|
|
|
167
120
|
### workitem — Work Items
|
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,45 +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 — 安装 Agent Skill**
|
|
89
|
-
|
|
90
|
-
> 推荐用于 AI Agent 场景;如果只是手动使用 CLI,则是可选步骤。Skill 会教 Agent 正确选择和执行 `meegle` 命令。
|
|
91
|
-
|
|
92
|
-
```bash
|
|
93
|
-
npx skills add larksuite/meegle-cli -y -g
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
**Step 3 — 持久化 host**
|
|
97
|
-
|
|
98
|
-
```bash
|
|
99
|
-
meegle config set host <host>
|
|
82
|
+
npx -y @lark-project/meegle@latest install --host <host> --device-code --lang zh
|
|
100
83
|
```
|
|
101
84
|
|
|
102
85
|
`<host>` 示例:`project.feishu.cn`、`meegle.com`,或自建租户域名(如 `your-tenant.example.com`)。
|
|
103
86
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
> 建议后台执行。命令会输出授权 URL —— 提取后发给用户,用户在浏览器完成授权后命令自动退出。
|
|
107
|
-
|
|
108
|
-
```bash
|
|
109
|
-
meegle auth login --device-code
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
如不想持久化 host,也可以每次显式传入:
|
|
87
|
+
Device Code 流程会输出授权 URL;把链接发给用户,并保持命令运行直到用户在浏览器完成授权。
|
|
113
88
|
|
|
114
|
-
|
|
115
|
-
meegle auth login --device-code --host <host>
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
**Step 5 — 验证**
|
|
89
|
+
验证:
|
|
119
90
|
|
|
120
91
|
```bash
|
|
121
92
|
meegle auth status
|
|
@@ -125,19 +96,7 @@ meegle auth status
|
|
|
125
96
|
|
|
126
97
|
## AI Agent Skill
|
|
127
98
|
|
|
128
|
-
`skills/meegle
|
|
129
|
-
|
|
130
|
-
### 安装
|
|
131
|
-
|
|
132
|
-
```bash
|
|
133
|
-
# 先装 CLI(skill 底层会调用 meegle 命令)
|
|
134
|
-
npm install -g @lark-project/meegle
|
|
135
|
-
|
|
136
|
-
# 再加载 skill —— 会自动探测已装的 Agent,并在各自的 skill 目录里登记
|
|
137
|
-
npx skills add larksuite/meegle-cli -y -g
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
`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,而不是从自然语言说明里猜命令形状。
|
|
141
100
|
|
|
142
101
|
### 覆盖内容
|
|
143
102
|
|
|
@@ -148,11 +107,9 @@ npx skills add larksuite/meegle-cli -y -g
|
|
|
148
107
|
- **SOP** — 创建工作项、节点流转、状态流转、更新字段等场景的分步剧本
|
|
149
108
|
- **授权守护** — 所有业务命令前强制先过 `meegle auth status`
|
|
150
109
|
|
|
151
|
-
完整内容见 [skills/meegle/SKILL.md](./skills/meegle/SKILL.md) 和 [skills/meegle/references/](./skills/meegle/references/)。
|
|
152
|
-
|
|
153
110
|
### 使用方式
|
|
154
111
|
|
|
155
|
-
|
|
112
|
+
安装向导执行完成后,直接用自然语言和 Agent 对话。例如:
|
|
156
113
|
|
|
157
114
|
```
|
|
158
115
|
帮我看一下 PROJ 空间本周待办里的 P0 需求
|
|
@@ -160,8 +117,6 @@ npx skills add larksuite/meegle-cli -y -g
|
|
|
160
117
|
|
|
161
118
|
Agent 会参考 skill,自动选择合适的 `meegle` 命令执行。配合 `--dry-run`(见 [安全](#安全与风险提示))可以在 Agent 真正执行副作用前先预览请求。
|
|
162
119
|
|
|
163
|
-
> 小提示:skill 的名字 `meegle` 和 CLI 二进制同名。文档里提到 **`meegle` skill** 时指 `skills/meegle/` 里的文件;提到 **`meegle` CLI** 时指你 PATH 上的 `meegle` 命令。
|
|
164
|
-
|
|
165
120
|
## 命令一览
|
|
166
121
|
|
|
167
122
|
### workitem — 工作项域
|
|
@@ -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",
|