@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 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** — Ships a bundled [AI Agent Skill](#ai-agent-skill) that teaches Trae, Claude Code, Cursor, Windsurf, Gemini CLI and other agents how to drive Meegle with one command. 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
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
- ### Install
50
+ Run the setup wizard:
51
51
 
52
52
  ```bash
53
- npm install -g @lark-project/meegle
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 steps you need.
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
- # (Optional) Persist the host so future logins skip the arrow-key picker
62
- meegle config set host <host>
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 `meegle auth login` uses an arrow-key host picker plus a browser OAuth callback both require a real TTY, so they will hang or fail in CI runners, pipes, and agent shells like Claude Code. Use the Device Code flow instead: it prints an authorization URL that the user opens in any browser.
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 config set host <host>
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
- Alternatively, pass the host inline each time without persisting it:
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/` is a drop-in **skill** that teaches AI Agents — Trae, Claude Code, Cursor, Windsurf, Gemini CLI, GitHub Copilot CLI how to operate Meegle through this CLI. It bundles the command catalog, MQL syntax, field-value conventions, rich-text Markdown rules, and standard operating procedures for common write flows.
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 installed, just ask the agent in natural language. For example, in Trae:
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 友好** — 附带一份 [AI Agent Skill](#ai-agent-skill),一条命令即可把 Meegle 操作手册喂给 Trae、Claude Code、Cursor、Windsurf、Gemini CLI 等主流 Agent。CLI 命令同时面向人类和 Agent 设计,结构化 JSON 输出、`--dry-run` 预览、`--device-code` 无 TTY 流程
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
- npm install -g @lark-project/meegle
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
- # (可选)持久化 host,后续登录就不用再选菜单
62
- meegle config set host <host>
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
- `meegle auth login` 默认走上下选择菜单 + 浏览器 OAuth 回调,依赖真实 TTY,在 CI Runner、管道、Claude Code 这类没有 TTY 的环境里会挂起或报错。这些场景请改用 Device Code 流程 —— 命令会输出授权 URL,让用户在浏览器里完成授权。
81
-
82
- **Step 1 — 安装 CLI**
79
+ 默认浏览器 OAuth 流程依赖真实 TTY。在 CI Runner、管道、Claude Code 这类环境里,使用同一个安装向导,但显式传入 host 并启用 Device Code 登录:
83
80
 
84
81
  ```bash
85
- npm install -g @lark-project/meegle
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
- **Step 3 — Device Code 登录**
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
- **Step 4 — 验证**
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/` 是一份可直接加载的 **skill**,用来教 AI Agent —— Trae、Claude Code、Cursor、Windsurf、Gemini CLI、GitHub Copilot CLI —— 通过本 CLI 操作 Meegle。它把命令目录、MQL 语法、字段值写法、富文本 Markdown 规则,以及常见写入流程的 SOP 全部打包好。
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
- 安装后直接用自然语言和 Agent 对话。例如 Trae:
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 };
Binary file
Binary file
Binary file
Binary file
Binary file
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
- try {
20
- fs.accessSync(binPath, fs.constants.X_OK);
21
- } catch {
22
- const detected = `${platform}-${arch}`;
23
- const isSupported = SUPPORTED.includes(detected);
24
- console.error(
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(", ")}`
33
- );
34
- process.exit(1);
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, process.argv.slice(2), { stdio: "inherit" });
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
- process.kill(process.pid, result.signal);
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.9",
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",