@deveco-test/deveco-code 0.3.0-4.3 → 1.0.1
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 +0 -105
- package/README.md +89 -293
- package/bin/deveco.exe +5 -0
- package/package.json +17 -7
- package/postinstall.mjs +111 -180
- package/bin/deveco +0 -206
package/CHANGELOG.md
CHANGED
|
@@ -1,110 +1,5 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## v0.2.0 (2026-09-20)
|
|
4
|
-
|
|
5
|
-
### DevEco Code 核心
|
|
6
|
-
|
|
7
|
-
#### 新特性
|
|
8
|
-
|
|
9
|
-
- 支持第三方 Provider 直接接入使用,DevEco Code内置模型改用使用账号登录与实名认证(`c6f4854`)
|
|
10
|
-
- worktree 特性默认开启(`c97716f`)
|
|
11
|
-
- 向生成 `.ets` 代码的 agent 注入 ArkTS/ArkUI 代码规范(`fc6488e`)
|
|
12
|
-
- 支持在非空目录中安全创建项目(`884c5ff`)
|
|
13
|
-
- 文档检索命令统一经由内置 CLI 代理(`1d76bfd`)
|
|
14
|
-
|
|
15
|
-
#### 优化
|
|
16
|
-
|
|
17
|
-
- 构建、运行、日志能力收敛至内置 DevEco CLI,统一由 `devecocli` 承接,新增日志清理、`--tail` 校验、创建冲突检测与模拟器自动启动(`3f6eb64`、`559922d`、`1d60720`)
|
|
18
|
-
- 更新 UI 验证 MCP(`b24c309`)
|
|
19
|
-
|
|
20
|
-
#### 缺陷修复
|
|
21
|
-
|
|
22
|
-
- 修复自动启动模拟器后未等待就绪即运行导致阻塞(`5473f6d`)
|
|
23
|
-
- 恢复内置 CLI 技能加载(`2574319`)
|
|
24
|
-
- 修复 Windows 下 Bun 虚拟路径识别(`9cb1dc2`)
|
|
25
|
-
- 修复日志清理未校验目标设备、失败被误报为成功(`8bc95e6`)
|
|
26
|
-
- 修复文档预览截断后无法读取完整内容(`e65b278`)
|
|
27
|
-
- 修复 goal 模式文档检索范围与 build 模式不一致(`f71cc15`)
|
|
28
|
-
- 快照排除 `.codegenie` 目录(`431d772`)
|
|
29
|
-
- 修复 trace 上报字段因空值被平台丢弃(`e5bc1fa`)
|
|
30
|
-
- 升级 Bun 至 1.4.2,修复 pty 状态卡在运行中、SSE 超时未捕获异常(`aeb4536`)
|
|
31
|
-
- 修复自动合并工作流在合并失败时仍提示成功(`9d90028`)
|
|
32
|
-
|
|
33
|
-
#### 文档
|
|
34
|
-
|
|
35
|
-
- 对齐内置 CLI 技能文档(`29799f4`)
|
|
36
|
-
|
|
37
|
-
### TUI 终端界面
|
|
38
|
-
|
|
39
|
-
#### 新特性
|
|
40
|
-
|
|
41
|
-
- 会话滚动至非边缘时显示跳转提示,点击可跳至最早/最新消息并重新吸附(`8f6bf1a`)
|
|
42
|
-
|
|
43
|
-
#### 缺陷修复
|
|
44
|
-
|
|
45
|
-
- 修复 token 耗尽时按 Esc 导致上下文丢失(`eee4fce`)
|
|
46
|
-
|
|
47
|
-
## v0.1.12 (2026-09-04)
|
|
48
|
-
|
|
49
|
-
### DevEco Code 核心
|
|
50
|
-
|
|
51
|
-
#### 新特性
|
|
52
|
-
|
|
53
|
-
- 云端模型配置无损保留,支持 `Ctrl+T` 切换推理强度档位,状态栏展示当前生效档位(`a1568e0`)
|
|
54
|
-
- 更新内置 DevEco CLI 至 v1.3.2(`acfaa13`)
|
|
55
|
-
|
|
56
|
-
#### 缺陷修复
|
|
57
|
-
|
|
58
|
-
- 修复推理模型的思考内容被误用作会话标题(`6a78fac`)
|
|
59
|
-
|
|
60
|
-
## v0.1.11 (2026-09-02)
|
|
61
|
-
|
|
62
|
-
### DevEco Code 核心
|
|
63
|
-
|
|
64
|
-
#### 缺陷修复
|
|
65
|
-
|
|
66
|
-
- 实名认证复核改为复用浏览器登录流程,简化重新验证操作(`849623b9ce`)
|
|
67
|
-
|
|
68
|
-
#### 文档
|
|
69
|
-
|
|
70
|
-
- Provider 配置示例改用通用占位符,消除误导性内置 provider 示例(`0e75e01467`)
|
|
71
|
-
|
|
72
|
-
### TUI 终端界面
|
|
73
|
-
|
|
74
|
-
#### 新特性
|
|
75
|
-
|
|
76
|
-
- 新增 `/usage` 和 `/stats` 命令,打开标签式面板展示用量与配额统计,含热力图、模型卡片及配额进度(`a063068cf2`)
|
|
77
|
-
|
|
78
|
-
## v0.1.10 (2026-08-29)
|
|
79
|
-
|
|
80
|
-
### DevEco Code 核心
|
|
81
|
-
|
|
82
|
-
#### 新特性
|
|
83
|
-
|
|
84
|
-
- 新增 `--auto`/`--yolo` 权限自动批准标志,跳过工具调用确认(`89c7804`)
|
|
85
|
-
- 更新内置 DevEco CLI 至 v1.3.0(`c95e744`、`ecd05b6`)
|
|
86
|
-
|
|
87
|
-
#### 优化
|
|
88
|
-
|
|
89
|
-
- 移除过时的 `DEVECO_DEFAULTS` 回退逻辑(`f353e9a`)
|
|
90
|
-
|
|
91
|
-
#### 缺陷修复
|
|
92
|
-
|
|
93
|
-
- 修复登录 URL 在 Windows 上无法通过 shell 打开的问题(`185b685`)
|
|
94
|
-
- 限制会话重试次数,配额耗尽时提前终止(`10f8246`)
|
|
95
|
-
|
|
96
|
-
### TUI 终端界面
|
|
97
|
-
|
|
98
|
-
#### 新特性
|
|
99
|
-
|
|
100
|
-
- 新增 subagent 监控侧边栏面板(`57be7fa`)
|
|
101
|
-
- 流式编辑统计改为按行计数(`690909f`)
|
|
102
|
-
|
|
103
|
-
#### 缺陷修复
|
|
104
|
-
|
|
105
|
-
- 修复模板化技能体的技能徽章渲染问题(`0c326df`)
|
|
106
|
-
- 优化收集对话框交互细节(`e1816523`)
|
|
107
|
-
|
|
108
3
|
## v0.1.9 (2026-08-20)
|
|
109
4
|
|
|
110
5
|
### DevEco Code 核心
|
package/README.md
CHANGED
|
@@ -1,333 +1,129 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<
|
|
2
|
+
<a href="https://opencode.ai">
|
|
3
|
+
<picture>
|
|
4
|
+
<source srcset="packages/console/app/src/asset/logo-ornate-dark.svg" media="(prefers-color-scheme: dark)">
|
|
5
|
+
<source srcset="packages/console/app/src/asset/logo-ornate-light.svg" media="(prefers-color-scheme: light)">
|
|
6
|
+
<img src="packages/console/app/src/asset/logo-ornate-light.svg" alt="OpenCode logo">
|
|
7
|
+
</picture>
|
|
8
|
+
</a>
|
|
3
9
|
</p>
|
|
4
|
-
<p align="center"
|
|
10
|
+
<p align="center">The open source AI coding agent.</p>
|
|
5
11
|
<p align="center">
|
|
6
|
-
<a href="
|
|
12
|
+
<a href="https://opencode.ai/discord"><img alt="Discord" src="https://img.shields.io/discord/1391832426048651334?style=flat-square&label=discord" /></a>
|
|
13
|
+
<a href="https://www.npmjs.com/package/opencode-ai"><img alt="npm" src="https://img.shields.io/npm/v/opencode-ai?style=flat-square" /></a>
|
|
14
|
+
<a href="https://github.com/anomalyco/opencode/actions/workflows/publish.yml"><img alt="Build status" src="https://img.shields.io/github/actions/workflow/status/anomalyco/opencode/publish.yml?style=flat-square&branch=dev" /></a>
|
|
7
15
|
</p>
|
|
8
16
|
|
|
9
17
|
<p align="center">
|
|
10
|
-
<a href="
|
|
11
|
-
<a href="
|
|
12
|
-
<
|
|
13
|
-
<
|
|
14
|
-
<a href="
|
|
15
|
-
<a href="
|
|
18
|
+
<a href="README.md">English</a> |
|
|
19
|
+
<a href="README.zh.md">简体中文</a> |
|
|
20
|
+
<a href="README.zht.md">繁體中文</a> |
|
|
21
|
+
<a href="README.ko.md">한국어</a> |
|
|
22
|
+
<a href="README.de.md">Deutsch</a> |
|
|
23
|
+
<a href="README.es.md">Español</a> |
|
|
24
|
+
<a href="README.fr.md">Français</a> |
|
|
25
|
+
<a href="README.it.md">Italiano</a> |
|
|
26
|
+
<a href="README.da.md">Dansk</a> |
|
|
27
|
+
<a href="README.ja.md">日本語</a> |
|
|
28
|
+
<a href="README.pl.md">Polski</a> |
|
|
29
|
+
<a href="README.ru.md">Русский</a> |
|
|
30
|
+
<a href="README.bs.md">Bosanski</a> |
|
|
31
|
+
<a href="README.ar.md">العربية</a> |
|
|
32
|
+
<a href="README.no.md">Norsk</a> |
|
|
33
|
+
<a href="README.br.md">Português (Brasil)</a> |
|
|
34
|
+
<a href="README.th.md">ไทย</a> |
|
|
35
|
+
<a href="README.tr.md">Türkçe</a> |
|
|
36
|
+
<a href="README.uk.md">Українська</a> |
|
|
37
|
+
<a href="README.bn.md">বাংলা</a> |
|
|
38
|
+
<a href="README.gr.md">Ελληνικά</a> |
|
|
39
|
+
<a href="README.vi.md">Tiếng Việt</a>
|
|
16
40
|
</p>
|
|
17
41
|
|
|
18
|
-
|
|
19
|
-
<img src="https://raw.gitcode.com/openharmony-sig/deveco-code/raw/develop/assets/readme/readme-screenshot.png" alt="DevEco Code" width="600">
|
|
20
|
-
</p>
|
|
21
|
-
|
|
22
|
-
***
|
|
23
|
-
|
|
24
|
-
## 快速开始
|
|
25
|
-
|
|
26
|
-
```bash
|
|
27
|
-
# 1. 安装
|
|
28
|
-
npm install -g @deveco/deveco-code
|
|
29
|
-
|
|
30
|
-
# 2. 启动
|
|
31
|
-
deveco
|
|
32
|
-
|
|
33
|
-
# 3. 开始对话 —— 在终端中直接描述你的 HarmonyOS 开发需求
|
|
34
|
-
提示词示例:
|
|
35
|
-
- 解释一下代码库的架构
|
|
36
|
-
- 帮我重构login_check这个函数
|
|
37
|
-
- 帮我检查并修复语法错误
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
> 建议使用 [npm 官方源](https://registry.npmjs.org/) 或 [淘宝镜像源](https://registry.npmmirror.com/) 安装,其他镜像源可能因同步延迟导致安装失败或版本滞后。
|
|
41
|
-
|
|
42
|
-
> 如需编译构建、设备运行等能力,请先安装 [DevEco Studio](https://developer.huawei.com/consumer/cn/deveco-studio/) 并配置 `DEVECO_HOME` 环境变量。
|
|
43
|
-
|
|
44
|
-
## 简介
|
|
45
|
-
|
|
46
|
-
DevEco Code 是一款面向 HarmonyOS 开发场景的 AI Agent 工具,支持代码编写、编译构建、设备运行、文档查阅、运行时调试及 ArkTS 问题修复等能力。
|
|
47
|
-
|
|
48
|
-
DevEco Code 基于开源项目 OpenCode 扩展开发,保留了 OpenCode 的终端交互、配置体系、 Provider / MCP / Skill / Plugin 等能力,并针对 HarmonyOS 工程增加了 DevEco Studio、Hvigor、HDC、Skill、HarmonyOS 知识库、ArkTS 检查和设备调试相关集成。
|
|
49
|
-
|
|
50
|
-
## 支持平台
|
|
51
|
-
|
|
52
|
-
DevEco Code 当前通过 npm 提供以下平台安装包:
|
|
53
|
-
|
|
54
|
-
| 平台 | 架构 | 说明 |
|
|
55
|
-
| --- | --- | --- |
|
|
56
|
-
| Windows | x64 | Windows 11 |
|
|
57
|
-
| macOS | arm64(Apple Silicon) | M 系列芯片 |
|
|
58
|
-
| macOS | x64(Intel) | Intel 芯片 Mac |
|
|
59
|
-
|
|
60
|
-
> 暂不支持 Linux。HarmonyOS 编译构建、模拟器与真机调试依赖 [DevEco Studio](https://developer.huawei.com/consumer/cn/deveco-studio/),且目前仅提供 Windows 与 macOS 版本。
|
|
61
|
-
|
|
62
|
-
## 推荐配置
|
|
63
|
-
|
|
64
|
-
**操作系统**
|
|
65
|
-
|
|
66
|
-
- **Windows**:Windows 11 22H2 及以上
|
|
67
|
-
- **macOS**:macOS 15 Sequoia 及以上
|
|
68
|
-
|
|
69
|
-
**硬件**
|
|
70
|
-
|
|
71
|
-
- **日常使用**(短会话、单模块改动、以对话与代码编辑为主):8 GB 及以上内存
|
|
72
|
-
- **重度使用**(大工程、长会话、频繁编译构建与模拟器/真机调试):16 GB 及以上内存;建议为 DevEco Studio、SDK、模拟器与构建缓存预留 20 GB 及以上可用磁盘空间
|
|
73
|
-
|
|
74
|
-
**运行时与工具链**
|
|
75
|
-
|
|
76
|
-
- [Node.js](https://nodejs.org) **22 及以上**
|
|
77
|
-
- [DevEco Studio](https://developer.huawei.com/consumer/cn/deveco-studio/) **6.1 及以上**(编译构建、Hvigor、HDC、模拟器/真机运行)
|
|
78
|
-
- 已配置 `DEVECO_HOME` 环境变量,指向 DevEco Studio 安装目录
|
|
79
|
-
|
|
80
|
-
**终端 Shell**
|
|
81
|
-
|
|
82
|
-
- Windows:PowerShell 7+(推荐)、Windows PowerShell 5.1+
|
|
83
|
-
- macOS:Zsh(推荐)、Bash
|
|
84
|
-
|
|
85
|
-
**网络**
|
|
86
|
-
|
|
87
|
-
- 需要稳定的互联网连接(华为账号登录、模型调用、HarmonyOS 知识库检索等)
|
|
88
|
-
|
|
89
|
-
## 安装前置
|
|
90
|
-
|
|
91
|
-
DevEco Code 通过 npm 分发,安装前请先准备以下环境:
|
|
92
|
-
|
|
93
|
-
1. 安装 [Node.js](https://nodejs.org),**推荐使用 22 及更高版本**
|
|
94
|
-
2. (可选)安装 [DevEco Studio](https://developer.huawei.com/consumer/cn/deveco-studio/),**推荐使用 6.1 及更高版本**;若不安装,HarmonyOS 应用构建、推包等工具将无法使用
|
|
95
|
-
3. (可选)配置 `DEVECO_HOME` 环境变量指向 DevEco Studio 安装目录,默认路径示例:
|
|
96
|
-
- **macOS**:`/Applications/DevEco-Studio.app`
|
|
97
|
-
- **Windows**:`C:\Program Files\Huawei\DevEco Studio`
|
|
98
|
-
|
|
99
|
-
可先在终端验证 Node.js 环境:
|
|
100
|
-
|
|
101
|
-
```bash
|
|
102
|
-
node -v
|
|
103
|
-
npm -v
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
## 安装与卸载
|
|
107
|
-
|
|
108
|
-
安装:
|
|
109
|
-
|
|
110
|
-
```bash
|
|
111
|
-
npm install -g @deveco/deveco-code
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
查看版本、启动:
|
|
115
|
-
|
|
116
|
-
```bash
|
|
117
|
-
deveco --version
|
|
118
|
-
deveco
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
更新:
|
|
122
|
-
|
|
123
|
-
```bash
|
|
124
|
-
deveco upgrade
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
卸载运行时数据与全局包:
|
|
128
|
-
|
|
129
|
-
```bash
|
|
130
|
-
deveco uninstall
|
|
131
|
-
npm uninstall -g @deveco/deveco-code
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
## 登录与登出
|
|
42
|
+
[](https://opencode.ai)
|
|
135
43
|
|
|
136
|
-
|
|
44
|
+
---
|
|
137
45
|
|
|
138
|
-
|
|
139
|
-
deveco auth login
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
登出会清除当前华为账号的本地登录状态,下次启动需重新登录。执行:
|
|
46
|
+
### Installation
|
|
143
47
|
|
|
144
48
|
```bash
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
## 模型配置
|
|
149
|
-
|
|
150
|
-
登录后可使用 DevEco Code 提供的免费模型通道。
|
|
49
|
+
# YOLO
|
|
50
|
+
curl -fsSL https://opencode.ai/install | bash
|
|
151
51
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
// 可选,缺省即为 openai-compatible;非 OpenAI 兼容的服务商才需要指定
|
|
163
|
-
"npm": "@ai-sdk/openai-compatible",
|
|
164
|
-
// 显示名称,可自定义
|
|
165
|
-
"name": "My Provider",
|
|
166
|
-
"models": {
|
|
167
|
-
// 替换为服务商提供的模型 ID
|
|
168
|
-
"<your-model-id>": {
|
|
169
|
-
"tool_call": true,
|
|
170
|
-
"limit": {
|
|
171
|
-
"context": 200000,
|
|
172
|
-
"output": 8192
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
},
|
|
176
|
-
"options": {
|
|
177
|
-
// 替换为服务商的 API 地址
|
|
178
|
-
"baseURL": "https://<your-endpoint>/v1",
|
|
179
|
-
// 替换为你的 API Key
|
|
180
|
-
"apiKey": "<your-api-key>"
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
}
|
|
52
|
+
# Package managers
|
|
53
|
+
npm i -g opencode-ai@latest # or bun/pnpm/yarn
|
|
54
|
+
scoop install opencode # Windows
|
|
55
|
+
choco install opencode # Windows
|
|
56
|
+
brew install anomalyco/tap/opencode # macOS and Linux (recommended, always up to date)
|
|
57
|
+
brew install opencode # macOS and Linux (official brew formula, updated less)
|
|
58
|
+
sudo pacman -S opencode # Arch Linux (Stable)
|
|
59
|
+
paru -S opencode-bin # Arch Linux (Latest from AUR)
|
|
60
|
+
mise use -g opencode # Any OS
|
|
61
|
+
nix run nixpkgs#opencode # or github:anomalyco/opencode for latest dev branch
|
|
185
62
|
```
|
|
186
63
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
UI 检查是功能验证阶段的可选能力,用于验证界面是否符合需求描述。
|
|
64
|
+
> [!TIP]
|
|
65
|
+
> Remove versions older than 0.1.x before installing.
|
|
190
66
|
|
|
191
|
-
|
|
67
|
+
### Desktop App (BETA)
|
|
192
68
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
```jsonc
|
|
196
|
-
{
|
|
197
|
-
"$schema": "https://opencode.ai/config.json",
|
|
198
|
-
"provider": {
|
|
199
|
-
"myprovider": {
|
|
200
|
-
"npm": "@ai-sdk/openai-compatible",
|
|
201
|
-
"name": "alibaba",
|
|
202
|
-
"options": {
|
|
203
|
-
"baseURL": "https://dashscope.aliyuncs.com/compatible-mode/v1",
|
|
204
|
-
"apiKey": "<your-api-key>",
|
|
205
|
-
},
|
|
206
|
-
"models": {
|
|
207
|
-
"qwen3-vl-plus": {
|
|
208
|
-
"modalities": {
|
|
209
|
-
"input": ["text", "image"],
|
|
210
|
-
"output": ["text"],
|
|
211
|
-
},
|
|
212
|
-
},
|
|
213
|
-
},
|
|
214
|
-
},
|
|
215
|
-
},
|
|
216
|
-
"agent": {
|
|
217
|
-
"ui_verification": {
|
|
218
|
-
"mode": "subagent",
|
|
219
|
-
"model": "myprovider/qwen3-vl-plus", // 格式为 <provider-id>/<model-id>
|
|
220
|
-
"hidden": true,
|
|
221
|
-
},
|
|
222
|
-
},
|
|
223
|
-
}
|
|
224
|
-
```
|
|
69
|
+
OpenCode is also available as a desktop application. Download directly from the [releases page](https://github.com/anomalyco/opencode/releases) or [opencode.ai/download](https://opencode.ai/download).
|
|
225
70
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
## Agent 配置
|
|
233
|
-
|
|
234
|
-
DevEco Code 面向 HarmonyOS 开发提供以下 Agent 配置(按 `Tab` 键切换):
|
|
235
|
-
|
|
236
|
-
- `Build`:默认模式,适合工程生成、代码生成、配置修正、测试执行、推包运行和发布执行
|
|
237
|
-
- `Plan`:适合需求拆解、技术方案、发布规划、测试规划和文档生成
|
|
238
|
-
- `Goal`:适合 SDD 五阶段从需求到实现与构建验证的端到端特性交付
|
|
239
|
-
|
|
240
|
-
## HarmonyOS 场景能力
|
|
241
|
-
|
|
242
|
-
DevEco Code 集成了常用 HarmonyOS 开发工具能力:
|
|
243
|
-
|
|
244
|
-
| 工具 | 说明 |
|
|
245
|
-
| ------------------------ | -------------------- |
|
|
246
|
-
| `build_project` | 执行编译构建并导出构建产物 |
|
|
247
|
-
| `start_app` | 在模拟器或真机上运行应用 |
|
|
248
|
-
| `hdc_log` | 收集/清理设备日志/查看连接模拟器 |
|
|
249
|
-
| `verify_ui` | 执行 UI 操作验证功能是否正确 |
|
|
250
|
-
| `switch_cwd` | 切换构建项目路径 |
|
|
251
|
-
|
|
252
|
-
常见场景包括:从零到一创建 HarmonyOS 工程、增量开发页面、修复编译报错、真机调试。
|
|
253
|
-
|
|
254
|
-
## 扩展能力
|
|
255
|
-
|
|
256
|
-
DevEco Code 兼容 OpenCode 的 Skill、MCP 和 Plugin 扩展方式。
|
|
257
|
-
|
|
258
|
-
> 新增或修改 Skill、MCP、Plugin 配置后,需退出并重新执行 `deveco` 启动后才会生效。
|
|
259
|
-
|
|
260
|
-
### Skills
|
|
71
|
+
| Platform | Download |
|
|
72
|
+
| --------------------- | ---------------------------------- |
|
|
73
|
+
| macOS (Apple Silicon) | `opencode-desktop-mac-arm64.dmg` |
|
|
74
|
+
| macOS (Intel) | `opencode-desktop-mac-x64.dmg` |
|
|
75
|
+
| Windows | `opencode-desktop-windows-x64.exe` |
|
|
76
|
+
| Linux | `.deb`, `.rpm`, or `.AppImage` |
|
|
261
77
|
|
|
262
78
|
```bash
|
|
263
|
-
|
|
79
|
+
# macOS (Homebrew)
|
|
80
|
+
brew install --cask opencode-desktop
|
|
81
|
+
# Windows (Scoop)
|
|
82
|
+
scoop bucket add extras; scoop install extras/opencode-desktop
|
|
264
83
|
```
|
|
265
84
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
### MCP
|
|
85
|
+
#### Installation Directory
|
|
269
86
|
|
|
270
|
-
|
|
87
|
+
The install script respects the following priority order for the installation path:
|
|
271
88
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
"playwright": {
|
|
277
|
-
"type": "local",
|
|
278
|
-
"command": ["npx", "@playwright/mcp@latest"],
|
|
279
|
-
"enabled": true
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
### Plugins
|
|
89
|
+
1. `$OPENCODE_INSTALL_DIR` - Custom installation directory
|
|
90
|
+
2. `$XDG_BIN_DIR` - XDG Base Directory Specification compliant path
|
|
91
|
+
3. `$HOME/bin` - Standard user binary directory (if it exists or can be created)
|
|
92
|
+
4. `$HOME/.opencode/bin` - Default fallback
|
|
286
93
|
|
|
287
94
|
```bash
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
然后在 `deveco.jsonc` 中配置插件入口:
|
|
292
|
-
|
|
293
|
-
```jsonc
|
|
294
|
-
{
|
|
295
|
-
"plugin": [
|
|
296
|
-
"node_modules/oh-my-opencode/dist/index.js"
|
|
297
|
-
]
|
|
298
|
-
}
|
|
95
|
+
# Examples
|
|
96
|
+
OPENCODE_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash
|
|
97
|
+
XDG_BIN_DIR=$HOME/.local/bin curl -fsSL https://opencode.ai/install | bash
|
|
299
98
|
```
|
|
300
99
|
|
|
301
|
-
|
|
100
|
+
### Agents
|
|
302
101
|
|
|
303
|
-
|
|
102
|
+
OpenCode includes two built-in agents you can switch between with the `Tab` key.
|
|
304
103
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
104
|
+
- **build** - Default, full-access agent for development work
|
|
105
|
+
- **plan** - Read-only agent for analysis and code exploration
|
|
106
|
+
- Denies file edits by default
|
|
107
|
+
- Asks permission before running bash commands
|
|
108
|
+
- Ideal for exploring unfamiliar codebases or planning changes
|
|
309
109
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
cp {源路径}/opencode.jsonc ~/.config/deveco/deveco.jsonc
|
|
313
|
-
```
|
|
110
|
+
Also included is a **general** subagent for complex searches and multistep tasks.
|
|
111
|
+
This is used internally and can be invoked using `@general` in messages.
|
|
314
112
|
|
|
315
|
-
|
|
113
|
+
Learn more about [agents](https://opencode.ai/docs/agents).
|
|
316
114
|
|
|
317
|
-
|
|
115
|
+
### Documentation
|
|
318
116
|
|
|
319
|
-
|
|
117
|
+
For more info on how to configure OpenCode, [**head over to our docs**](https://opencode.ai/docs).
|
|
320
118
|
|
|
321
|
-
|
|
119
|
+
### Contributing
|
|
322
120
|
|
|
323
|
-
|
|
324
|
-
- 常见问题请参阅 [FAQ 文档](https://gitcode.com/openharmony-sig/deveco-code/wiki/FAQ.md)
|
|
325
|
-
- 反馈与交流 [GitCode Issue](https://gitcode.com/openharmony-sig/deveco-code/issues)
|
|
121
|
+
If you're interested in contributing to OpenCode, please read our [contributing docs](./CONTRIBUTING.md) before submitting a pull request.
|
|
326
122
|
|
|
327
|
-
|
|
123
|
+
### Building on OpenCode
|
|
328
124
|
|
|
329
|
-
|
|
125
|
+
If you are working on a project that's related to OpenCode and is using "opencode" as part of its name, for example "opencode-dashboard" or "opencode-mobile", please add a note to your README to clarify that it is not built by the OpenCode team and is not affiliated with us in any way.
|
|
330
126
|
|
|
331
|
-
|
|
127
|
+
---
|
|
332
128
|
|
|
333
|
-
|
|
129
|
+
**Join our community** [Discord](https://discord.gg/opencode) | [X.com](https://x.com/opencode)
|
package/bin/deveco.exe
ADDED
package/package.json
CHANGED
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deveco-test/deveco-code",
|
|
3
3
|
"bin": {
|
|
4
|
-
"deveco": "./bin/deveco"
|
|
4
|
+
"deveco": "./bin/deveco.exe"
|
|
5
5
|
},
|
|
6
6
|
"scripts": {
|
|
7
|
-
"postinstall": "
|
|
7
|
+
"postinstall": "node ./postinstall.mjs"
|
|
8
8
|
},
|
|
9
|
-
"version": "
|
|
9
|
+
"version": "1.0.1",
|
|
10
10
|
"license": "MIT",
|
|
11
|
+
"os": [
|
|
12
|
+
"darwin",
|
|
13
|
+
"linux",
|
|
14
|
+
"win32",
|
|
15
|
+
"openharmony"
|
|
16
|
+
],
|
|
17
|
+
"cpu": [
|
|
18
|
+
"arm64",
|
|
19
|
+
"x64"
|
|
20
|
+
],
|
|
11
21
|
"files": [
|
|
12
22
|
"bin/**/*",
|
|
13
23
|
"postinstall.mjs",
|
|
@@ -16,10 +26,10 @@
|
|
|
16
26
|
"CHANGELOG.md"
|
|
17
27
|
],
|
|
18
28
|
"optionalDependencies": {
|
|
19
|
-
"@deveco-test/deveco-
|
|
20
|
-
"@deveco-test/deveco-
|
|
21
|
-
"@deveco-test/deveco-
|
|
22
|
-
"@deveco-test/deveco-code-
|
|
29
|
+
"@deveco-test/deveco-windows-x64": "1.0.1",
|
|
30
|
+
"@deveco-test/deveco-darwin-x64": "1.0.1",
|
|
31
|
+
"@deveco-test/deveco-darwin-arm64": "1.0.1",
|
|
32
|
+
"@deveco-test/deveco-code-openharmony-arm64": "1.0.1"
|
|
23
33
|
},
|
|
24
34
|
"dependencies": {
|
|
25
35
|
"@deveco/deveco-cli": "1.3.4"
|
package/postinstall.mjs
CHANGED
|
@@ -1,53 +1,29 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import childProcess from "child_process"
|
|
4
|
-
import fs from "fs"
|
|
5
|
-
import os from "os"
|
|
6
|
-
import path from "path"
|
|
7
|
-
import { createRequire } from "module"
|
|
8
|
-
import { fileURLToPath } from "url"
|
|
9
|
-
|
|
10
|
-
const
|
|
3
|
+
import childProcess from "node:child_process"
|
|
4
|
+
import fs from "node:fs"
|
|
5
|
+
import os from "node:os"
|
|
6
|
+
import path from "node:path"
|
|
7
|
+
import { createRequire } from "node:module"
|
|
8
|
+
import { fileURLToPath } from "node:url"
|
|
9
|
+
|
|
10
|
+
const directory = path.dirname(fileURLToPath(import.meta.url))
|
|
11
11
|
const require = createRequire(import.meta.url)
|
|
12
|
-
const packageJson = JSON.parse(fs.readFileSync(path.join(
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const platform = platformMap[os.platform()] ?? os.platform()
|
|
26
|
-
const arch = archMap[os.arch()] ?? os.arch()
|
|
27
|
-
const scope = packageJson.name && packageJson.name.includes("/") ? packageJson.name.split("/")[0] : "@deveco"
|
|
28
|
-
const base = `${scope}/deveco-code-${platform}-${arch}`
|
|
29
|
-
const sourceBinary = platform === "windows" ? "deveco.exe" : "deveco"
|
|
30
|
-
const targetBinary = path.join(__dirname, "bin", "deveco.exe")
|
|
31
|
-
const devecoCliPackageName = "@deveco/deveco-cli"
|
|
32
|
-
|
|
33
|
-
function resolveLocalDevecoCliEntry() {
|
|
34
|
-
const packageJsonPath = require.resolve(`${devecoCliPackageName}/package.json`)
|
|
35
|
-
const root = path.dirname(packageJsonPath)
|
|
36
|
-
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"))
|
|
37
|
-
const entry = path.join(root, pkg.bin?.devecocli ?? "dist/cli.js")
|
|
38
|
-
if (fs.existsSync(entry)) return entry
|
|
39
|
-
throw new Error(`Local dependency "${devecoCliPackageName}" has no CLI entry at ${entry}.`)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function writeDevecoCliEntryMarker(installRoot) {
|
|
43
|
-
const marker = path.join(installRoot, "vendor", ".deveco-cli-entry")
|
|
44
|
-
fs.mkdirSync(path.dirname(marker), { recursive: true })
|
|
45
|
-
fs.writeFileSync(marker, `${resolveLocalDevecoCliEntry()}\n`, "utf8")
|
|
46
|
-
}
|
|
12
|
+
const packageJson = JSON.parse(fs.readFileSync(path.join(directory, "package.json"), "utf8"))
|
|
13
|
+
const command = Object.keys(packageJson.bin ?? {})[0]
|
|
14
|
+
if (!command) throw new Error("OpenCode package does not declare a binary")
|
|
15
|
+
const sourceCommand = packageJson.opencodeSourceBinary ?? command
|
|
16
|
+
|
|
17
|
+
const platform = { darwin: "darwin", linux: "linux", win32: "windows" }[os.platform()] ?? os.platform()
|
|
18
|
+
const arch = { x64: "x64", arm64: "arm64", arm: "arm" }[os.arch()] ?? os.arch()
|
|
19
|
+
const sourceBinary = platform === "windows" ? `${sourceCommand}.exe` : sourceCommand
|
|
20
|
+
const targetBinary = path.resolve(directory, packageJson.bin[command])
|
|
21
|
+
const dependencies = packageJson.optionalDependencies ?? {}
|
|
22
|
+
const base = Object.keys(dependencies).find((name) => name.endsWith(`-${platform}-${arch}`))
|
|
23
|
+
if (!base) throw new Error(`OpenCode does not provide a binary for ${platform}-${arch}`)
|
|
47
24
|
|
|
48
25
|
function supportsAvx2() {
|
|
49
26
|
if (arch !== "x64") return false
|
|
50
|
-
|
|
51
27
|
if (platform === "linux") {
|
|
52
28
|
try {
|
|
53
29
|
return /(^|\s)avx2(\s|$)/i.test(fs.readFileSync("/proc/cpuinfo", "utf8"))
|
|
@@ -55,27 +31,23 @@ function supportsAvx2() {
|
|
|
55
31
|
return false
|
|
56
32
|
}
|
|
57
33
|
}
|
|
58
|
-
|
|
59
34
|
if (platform === "darwin") {
|
|
60
35
|
try {
|
|
61
36
|
const result = childProcess.spawnSync("sysctl", ["-n", "hw.optional.avx2_0"], {
|
|
62
37
|
encoding: "utf8",
|
|
63
38
|
timeout: 1500,
|
|
64
39
|
})
|
|
65
|
-
|
|
66
|
-
return (result.stdout || "").trim() === "1"
|
|
40
|
+
return result.status === 0 && (result.stdout || "").trim() === "1"
|
|
67
41
|
} catch {
|
|
68
42
|
return false
|
|
69
43
|
}
|
|
70
44
|
}
|
|
71
|
-
|
|
72
45
|
if (platform === "windows") {
|
|
73
|
-
const
|
|
46
|
+
const script =
|
|
74
47
|
'(Add-Type -MemberDefinition "[DllImport(""kernel32.dll"")] public static extern bool IsProcessorFeaturePresent(int ProcessorFeature);" -Name Kernel32 -Namespace Win32 -PassThru)::IsProcessorFeaturePresent(40)'
|
|
75
|
-
|
|
76
48
|
for (const executable of ["powershell.exe", "pwsh.exe", "pwsh", "powershell"]) {
|
|
77
49
|
try {
|
|
78
|
-
const result = childProcess.spawnSync(executable, ["-NoProfile", "-NonInteractive", "-Command",
|
|
50
|
+
const result = childProcess.spawnSync(executable, ["-NoProfile", "-NonInteractive", "-Command", script], {
|
|
79
51
|
encoding: "utf8",
|
|
80
52
|
timeout: 3000,
|
|
81
53
|
windowsHide: true,
|
|
@@ -89,20 +61,13 @@ function supportsAvx2() {
|
|
|
89
61
|
}
|
|
90
62
|
}
|
|
91
63
|
}
|
|
92
|
-
|
|
93
64
|
return false
|
|
94
65
|
}
|
|
95
66
|
|
|
96
67
|
function isMusl() {
|
|
97
68
|
if (platform !== "linux") return false
|
|
98
|
-
|
|
99
69
|
try {
|
|
100
70
|
if (fs.existsSync("/etc/alpine-release")) return true
|
|
101
|
-
} catch {
|
|
102
|
-
// Ignore filesystem probes that are blocked by the host.
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
try {
|
|
106
71
|
const result = childProcess.spawnSync("ldd", ["--version"], { encoding: "utf8" })
|
|
107
72
|
return `${result.stdout || ""}${result.stderr || ""}`.toLowerCase().includes("musl")
|
|
108
73
|
} catch {
|
|
@@ -112,164 +77,130 @@ function isMusl() {
|
|
|
112
77
|
|
|
113
78
|
function packageNames() {
|
|
114
79
|
const baseline = arch === "x64" && !supportsAvx2()
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
return [
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
if (!fs.existsSync(binaryPath)) throw new Error(`Binary not found at ${binaryPath}`)
|
|
144
|
-
return binaryPath
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
function copyDir(src, dst) {
|
|
148
|
-
if (!fs.existsSync(dst)) fs.mkdirSync(dst, { recursive: true })
|
|
149
|
-
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
150
|
-
const srcPath = path.join(src, entry.name)
|
|
151
|
-
const dstPath = path.join(dst, entry.name)
|
|
152
|
-
if (entry.isDirectory()) {
|
|
153
|
-
copyDir(srcPath, dstPath)
|
|
154
|
-
} else {
|
|
155
|
-
fs.copyFileSync(srcPath, dstPath)
|
|
156
|
-
if (process.platform !== "win32") {
|
|
157
|
-
fs.chmodSync(dstPath, fs.statSync(srcPath).mode)
|
|
158
|
-
}
|
|
159
|
-
}
|
|
80
|
+
const names =
|
|
81
|
+
platform === "linux"
|
|
82
|
+
? isMusl()
|
|
83
|
+
? arch === "x64"
|
|
84
|
+
? baseline
|
|
85
|
+
? [`${base}-baseline-musl`, `${base}-musl`, `${base}-baseline`, base]
|
|
86
|
+
: [`${base}-musl`, `${base}-baseline-musl`, base, `${base}-baseline`]
|
|
87
|
+
: [`${base}-musl`, base]
|
|
88
|
+
: arch === "x64"
|
|
89
|
+
? baseline
|
|
90
|
+
? [`${base}-baseline`, base, `${base}-baseline-musl`, `${base}-musl`]
|
|
91
|
+
: [base, `${base}-baseline`, `${base}-musl`, `${base}-baseline-musl`]
|
|
92
|
+
: [base, `${base}-musl`]
|
|
93
|
+
: arch === "x64"
|
|
94
|
+
? baseline
|
|
95
|
+
? [`${base}-baseline`, base]
|
|
96
|
+
: [base, `${base}-baseline`]
|
|
97
|
+
: [base]
|
|
98
|
+
return names.filter((name) => dependencies[name])
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function copyBinary(source) {
|
|
102
|
+
if (!fs.existsSync(source)) throw new Error(`Binary not found at ${source}`)
|
|
103
|
+
fs.mkdirSync(path.dirname(targetBinary), { recursive: true })
|
|
104
|
+
if (fs.existsSync(targetBinary)) {
|
|
105
|
+
try {
|
|
106
|
+
fs.unlinkSync(targetBinary)
|
|
107
|
+
} catch {}
|
|
160
108
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
const vendorDst = path.join(__dirname, "vendor")
|
|
166
|
-
if (fs.existsSync(vendorSrc)) {
|
|
167
|
-
copyDir(vendorSrc, vendorDst)
|
|
168
|
-
// Windows packs often drop Unix +x; force it back on macOS/Linux install.
|
|
169
|
-
ensureVendorExecutables(vendorDst)
|
|
170
|
-
ensureVendorExecutables(path.join(packageDir, "vendor"))
|
|
109
|
+
try {
|
|
110
|
+
fs.linkSync(source, targetBinary)
|
|
111
|
+
} catch {
|
|
112
|
+
fs.copyFileSync(source, targetBinary)
|
|
171
113
|
}
|
|
114
|
+
fs.chmodSync(targetBinary, 0o755)
|
|
172
115
|
}
|
|
173
116
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
let entries
|
|
182
|
-
try {
|
|
183
|
-
entries = fs.readdirSync(dir, { withFileTypes: true })
|
|
184
|
-
} catch {
|
|
185
|
-
continue
|
|
186
|
-
}
|
|
187
|
-
for (const entry of entries) {
|
|
188
|
-
const full = path.join(dir, entry.name)
|
|
189
|
-
if (entry.isDirectory()) {
|
|
190
|
-
stack.push(full)
|
|
191
|
-
continue
|
|
192
|
-
}
|
|
193
|
-
if (!entry.isFile()) continue
|
|
194
|
-
// Top-level vendor/ripgrep/rg and nested @vscode/ripgrep*/**/rg
|
|
195
|
-
if (entry.name === "rg") {
|
|
196
|
-
try {
|
|
197
|
-
fs.chmodSync(full, 0o755)
|
|
198
|
-
} catch {
|
|
199
|
-
// Best-effort; ignore files we cannot chmod.
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
}
|
|
117
|
+
function resolveBinary(name) {
|
|
118
|
+
const packagePath = require.resolve(`${name}/package.json`)
|
|
119
|
+
// Package managers keep an older platform package when the matching version is not yet on the registry,
|
|
120
|
+
// which would silently install the previous release under the new launcher.
|
|
121
|
+
const version = JSON.parse(fs.readFileSync(packagePath, "utf8")).version
|
|
122
|
+
if (version !== dependencies[name]) throw new Error(`${name} is ${version}, expected ${dependencies[name]}`)
|
|
123
|
+
return path.join(path.dirname(packagePath), "bin", sourceBinary)
|
|
204
124
|
}
|
|
205
125
|
|
|
206
126
|
function installPackage(name) {
|
|
207
|
-
const version = packageJson.optionalDependencies?.[name]
|
|
208
|
-
if (!version) return
|
|
209
|
-
|
|
210
127
|
const temp = fs.mkdtempSync(path.join(os.tmpdir(), "opencode-install-"))
|
|
211
128
|
try {
|
|
212
129
|
const result = childProcess.spawnSync(
|
|
213
130
|
"npm",
|
|
214
|
-
[
|
|
131
|
+
[
|
|
132
|
+
"install",
|
|
133
|
+
"--ignore-scripts",
|
|
134
|
+
"--no-save",
|
|
135
|
+
"--loglevel=error",
|
|
136
|
+
"--prefix",
|
|
137
|
+
temp,
|
|
138
|
+
`${name}@${dependencies[name]}`,
|
|
139
|
+
],
|
|
215
140
|
{ stdio: "inherit", windowsHide: true },
|
|
216
141
|
)
|
|
217
|
-
if (result.status !== 0) return
|
|
218
|
-
|
|
219
|
-
copyBinary(path.join(packageDir, "bin", sourceBinary), targetBinary)
|
|
220
|
-
copyVendor(packageDir)
|
|
142
|
+
if (result.status !== 0) return false
|
|
143
|
+
copyBinary(path.join(temp, "node_modules", name, "bin", sourceBinary))
|
|
221
144
|
return true
|
|
222
145
|
} finally {
|
|
223
146
|
fs.rmSync(temp, { recursive: true, force: true })
|
|
224
147
|
}
|
|
225
148
|
}
|
|
226
149
|
|
|
227
|
-
function
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
150
|
+
function verifyBinary() {
|
|
151
|
+
return (
|
|
152
|
+
childProcess.spawnSync(targetBinary, ["--version"], {
|
|
153
|
+
stdio: "ignore",
|
|
154
|
+
windowsHide: true,
|
|
155
|
+
}).status === 0
|
|
156
|
+
)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// DevEco fork (v1 parity): the bundled devecocli shim resolves its CLI entry through an
|
|
160
|
+
// absolute marker written at install time; the @deveco/deveco-cli dependency provides it.
|
|
161
|
+
function writeDevecoCliEntryMarker() {
|
|
231
162
|
try {
|
|
232
|
-
|
|
163
|
+
const cliRoot = path.dirname(require.resolve("@deveco/deveco-cli/package.json"))
|
|
164
|
+
const manifest = JSON.parse(fs.readFileSync(path.join(cliRoot, "package.json"), "utf8"))
|
|
165
|
+
const declared = typeof manifest.bin === "string" ? manifest.bin : manifest.bin?.devecocli
|
|
166
|
+
const candidates = [declared && path.join(cliRoot, declared), path.join(cliRoot, "dist", "cli.js")].filter(
|
|
167
|
+
(file) => file,
|
|
168
|
+
)
|
|
169
|
+
const entry = candidates.find((file) => fs.existsSync(file))
|
|
170
|
+
if (!entry) return
|
|
171
|
+
const vendorDir = path.join(directory, "vendor")
|
|
172
|
+
fs.mkdirSync(vendorDir, { recursive: true })
|
|
173
|
+
fs.writeFileSync(path.join(vendorDir, ".deveco-cli-entry"), `${path.resolve(entry)}\n`)
|
|
233
174
|
} catch {
|
|
234
|
-
|
|
175
|
+
// Best-effort: without the marker the shim falls back to the other resolution layouts.
|
|
235
176
|
}
|
|
236
|
-
fs.chmodSync(target, 0o755)
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
function verifyBinary() {
|
|
240
|
-
const result = childProcess.spawnSync(targetBinary, ["--version"], {
|
|
241
|
-
encoding: "utf8",
|
|
242
|
-
stdio: "ignore",
|
|
243
|
-
windowsHide: true,
|
|
244
|
-
})
|
|
245
|
-
return result.status === 0
|
|
246
177
|
}
|
|
247
178
|
|
|
248
179
|
function main() {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
for (const name of packageNames()) {
|
|
180
|
+
const names = packageNames()
|
|
181
|
+
for (const name of names) {
|
|
252
182
|
try {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
183
|
+
copyBinary(resolveBinary(name))
|
|
184
|
+
if (verifyBinary()) {
|
|
185
|
+
writeDevecoCliEntryMarker()
|
|
186
|
+
return
|
|
187
|
+
}
|
|
258
188
|
} catch {
|
|
259
|
-
if (installPackage(name) && verifyBinary())
|
|
189
|
+
if (installPackage(name) && verifyBinary()) {
|
|
190
|
+
writeDevecoCliEntryMarker()
|
|
191
|
+
return
|
|
192
|
+
}
|
|
260
193
|
}
|
|
261
194
|
}
|
|
262
195
|
|
|
263
196
|
throw new Error(
|
|
264
|
-
`
|
|
265
|
-
.map((name) => JSON.stringify(name))
|
|
266
|
-
.join(" or ")}.`,
|
|
197
|
+
`Failed to install OpenCode. Try manually installing ${names.map((name) => JSON.stringify(name)).join(" or ")}.`,
|
|
267
198
|
)
|
|
268
199
|
}
|
|
269
200
|
|
|
270
201
|
try {
|
|
271
202
|
main()
|
|
272
203
|
} catch (error) {
|
|
273
|
-
console.error(error.message)
|
|
204
|
+
console.error(error instanceof Error ? error.message : String(error))
|
|
274
205
|
process.exit(1)
|
|
275
206
|
}
|
package/bin/deveco
DELETED
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
const childProcess = require("child_process")
|
|
4
|
-
const fs = require("fs")
|
|
5
|
-
const path = require("path")
|
|
6
|
-
const os = require("os")
|
|
7
|
-
|
|
8
|
-
const forwardedSignals = ["SIGINT", "SIGTERM", "SIGHUP"]
|
|
9
|
-
|
|
10
|
-
function run(target) {
|
|
11
|
-
const child = childProcess.spawn(target, process.argv.slice(2), {
|
|
12
|
-
stdio: "inherit",
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
child.on("error", (error) => {
|
|
16
|
-
console.error(error.message)
|
|
17
|
-
process.exit(1)
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
const forwarders = {}
|
|
21
|
-
for (const signal of forwardedSignals) {
|
|
22
|
-
forwarders[signal] = () => {
|
|
23
|
-
try {
|
|
24
|
-
child.kill(signal)
|
|
25
|
-
} catch {
|
|
26
|
-
// The child may have already exited.
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
process.on(signal, forwarders[signal])
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
child.on("exit", (code, signal) => {
|
|
33
|
-
for (const forwardedSignal of forwardedSignals) {
|
|
34
|
-
process.removeListener(forwardedSignal, forwarders[forwardedSignal])
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (signal) {
|
|
38
|
-
process.kill(process.pid, signal)
|
|
39
|
-
return
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
process.exit(typeof code === "number" ? code : 0)
|
|
43
|
-
})
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const envPath = process.env.DEVECO_BIN_PATH
|
|
47
|
-
|
|
48
|
-
const scriptPath = fs.realpathSync(__filename)
|
|
49
|
-
const scriptDir = path.dirname(scriptPath)
|
|
50
|
-
|
|
51
|
-
//
|
|
52
|
-
const cached = path.join(scriptDir, ".deveco")
|
|
53
|
-
|
|
54
|
-
const platformMap = {
|
|
55
|
-
darwin: "darwin",
|
|
56
|
-
linux: "linux",
|
|
57
|
-
win32: "windows",
|
|
58
|
-
}
|
|
59
|
-
const archMap = {
|
|
60
|
-
x64: "x64",
|
|
61
|
-
arm64: "arm64",
|
|
62
|
-
arm: "arm",
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
let platform = platformMap[os.platform()]
|
|
66
|
-
if (!platform) {
|
|
67
|
-
platform = os.platform()
|
|
68
|
-
}
|
|
69
|
-
let arch = archMap[os.arch()]
|
|
70
|
-
if (!arch) {
|
|
71
|
-
arch = os.arch()
|
|
72
|
-
}
|
|
73
|
-
let mainPkg = {}
|
|
74
|
-
try {
|
|
75
|
-
mainPkg = JSON.parse(fs.readFileSync(path.join(scriptDir, "..", "package.json"), "utf8"))
|
|
76
|
-
} catch {
|
|
77
|
-
// dev mode or missing package.json
|
|
78
|
-
}
|
|
79
|
-
const scope = mainPkg.name && mainPkg.name.includes("/") ? mainPkg.name.split("/")[0] : "@deveco"
|
|
80
|
-
const base = scope + "/deveco-code-" + platform + "-" + arch
|
|
81
|
-
const binary = platform === "windows" ? "deveco.exe" : "deveco"
|
|
82
|
-
|
|
83
|
-
function supportsAvx2() {
|
|
84
|
-
if (arch !== "x64") return false
|
|
85
|
-
|
|
86
|
-
if (platform === "linux") {
|
|
87
|
-
try {
|
|
88
|
-
return /(^|\s)avx2(\s|$)/i.test(fs.readFileSync("/proc/cpuinfo", "utf8"))
|
|
89
|
-
} catch {
|
|
90
|
-
return false
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (platform === "darwin") {
|
|
95
|
-
try {
|
|
96
|
-
const result = childProcess.spawnSync("sysctl", ["-n", "hw.optional.avx2_0"], {
|
|
97
|
-
encoding: "utf8",
|
|
98
|
-
timeout: 1500,
|
|
99
|
-
})
|
|
100
|
-
if (result.status !== 0) return false
|
|
101
|
-
return (result.stdout || "").trim() === "1"
|
|
102
|
-
} catch {
|
|
103
|
-
return false
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (platform === "windows") {
|
|
108
|
-
const cmd =
|
|
109
|
-
'(Add-Type -MemberDefinition "[DllImport(""kernel32.dll"")] public static extern bool IsProcessorFeaturePresent(int ProcessorFeature);" -Name Kernel32 -Namespace Win32 -PassThru)::IsProcessorFeaturePresent(40)'
|
|
110
|
-
|
|
111
|
-
for (const exe of ["powershell.exe", "pwsh.exe", "pwsh", "powershell"]) {
|
|
112
|
-
try {
|
|
113
|
-
const result = childProcess.spawnSync(exe, ["-NoProfile", "-NonInteractive", "-Command", cmd], {
|
|
114
|
-
encoding: "utf8",
|
|
115
|
-
timeout: 3000,
|
|
116
|
-
windowsHide: true,
|
|
117
|
-
})
|
|
118
|
-
if (result.status !== 0) continue
|
|
119
|
-
const out = (result.stdout || "").trim().toLowerCase()
|
|
120
|
-
if (out === "true" || out === "1") return true
|
|
121
|
-
if (out === "false" || out === "0") return false
|
|
122
|
-
} catch {
|
|
123
|
-
continue
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
return false
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return false
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
const names = (() => {
|
|
134
|
-
const avx2 = supportsAvx2()
|
|
135
|
-
const baseline = arch === "x64" && !avx2
|
|
136
|
-
|
|
137
|
-
if (platform === "linux") {
|
|
138
|
-
const musl = (() => {
|
|
139
|
-
try {
|
|
140
|
-
if (fs.existsSync("/etc/alpine-release")) return true
|
|
141
|
-
} catch {
|
|
142
|
-
// ignore
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
try {
|
|
146
|
-
const result = childProcess.spawnSync("ldd", ["--version"], { encoding: "utf8" })
|
|
147
|
-
const text = ((result.stdout || "") + (result.stderr || "")).toLowerCase()
|
|
148
|
-
if (text.includes("musl")) return true
|
|
149
|
-
} catch {
|
|
150
|
-
// ignore
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return false
|
|
154
|
-
})()
|
|
155
|
-
|
|
156
|
-
if (musl) {
|
|
157
|
-
if (arch === "x64") {
|
|
158
|
-
if (baseline) return [`${base}-baseline-musl`, `${base}-musl`, `${base}-baseline`, base]
|
|
159
|
-
return [`${base}-musl`, `${base}-baseline-musl`, base, `${base}-baseline`]
|
|
160
|
-
}
|
|
161
|
-
return [`${base}-musl`, base]
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
if (arch === "x64") {
|
|
165
|
-
if (baseline) return [`${base}-baseline`, base, `${base}-baseline-musl`, `${base}-musl`]
|
|
166
|
-
return [base, `${base}-baseline`, `${base}-musl`, `${base}-baseline-musl`]
|
|
167
|
-
}
|
|
168
|
-
return [base, `${base}-musl`]
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
if (arch === "x64") {
|
|
172
|
-
if (baseline) return [`${base}-baseline`, base]
|
|
173
|
-
return [base, `${base}-baseline`]
|
|
174
|
-
}
|
|
175
|
-
return [base]
|
|
176
|
-
})()
|
|
177
|
-
|
|
178
|
-
function findBinary(startDir) {
|
|
179
|
-
let current = startDir
|
|
180
|
-
for (;;) {
|
|
181
|
-
const modules = path.join(current, "node_modules")
|
|
182
|
-
if (fs.existsSync(modules)) {
|
|
183
|
-
for (const name of names) {
|
|
184
|
-
const candidate = path.join(modules, name, "bin", binary)
|
|
185
|
-
if (fs.existsSync(candidate)) return candidate
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
const parent = path.dirname(current)
|
|
189
|
-
if (parent === current) {
|
|
190
|
-
return
|
|
191
|
-
}
|
|
192
|
-
current = parent
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
const resolved = envPath || (fs.existsSync(cached) ? cached : findBinary(scriptDir))
|
|
197
|
-
if (!resolved) {
|
|
198
|
-
console.error(
|
|
199
|
-
"It seems that your package manager failed to install the right version of the deveco CLI for your platform. You can try manually installing " +
|
|
200
|
-
names.map((n) => `\"${n}\"`).join(" or ") +
|
|
201
|
-
" package",
|
|
202
|
-
)
|
|
203
|
-
process.exit(1)
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
run(resolved)
|