@yep-ai/cli 0.0.0-stage → 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/README.md +91 -2
- package/bin/clients.js +77 -0
- package/bin/market.js +1621 -0
- package/bin/yep.js +11 -0
- package/lib/device/client.js +472 -0
- package/lib/device/command.js +93 -0
- package/lib/device/tools.js +343 -0
- package/package.json +30 -4
package/README.md
CHANGED
|
@@ -1,3 +1,92 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @yep-ai/cli · Yep Marketplace CLI
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`@yep-ai/cli` 提供统一的 `yep` 命令,支持市场操作、Git 内容版本和设备节点。需要 Node.js 22+。
|
|
4
|
+
|
|
5
|
+
## 安装与使用
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npx --yes @yep-ai/cli@latest --help
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
通过 npx 直接运行 npm 最新版,无需全局安装:
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
npx --yes @yep-ai/cli@latest --help
|
|
15
|
+
npx --yes @yep-ai/cli@latest device start --server https://yep.nihao.cool
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
本包包含完整实现。`yep-cli` 是计划中的同版本入口包,当前尚未发布本项目版本,请使用 `@yep-ai/cli`。
|
|
19
|
+
|
|
20
|
+
## 开发与本地安装
|
|
21
|
+
|
|
22
|
+
仓库根目录执行:
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
npm install
|
|
26
|
+
npm run yep -- --help
|
|
27
|
+
npm run pack:cli
|
|
28
|
+
npm install --prefix ./tmp/yep-install ./dist/cli/scoped/yep-ai-cli-1.0.1.tgz ./dist/cli/unscoped/yep-cli-1.0.1.tgz
|
|
29
|
+
./tmp/yep-install/node_modules/.bin/yep --version
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
完整实现包为 `scoped/yep-ai-cli-1.0.1.tgz`,入口包为 `unscoped/yep-cli-1.0.1.tgz`。本地验证时可一起指定两份文件,无需从 npm 下载实现包。输出的 `manifest.json` 记录包名、版本、依赖和 SHA-256。
|
|
33
|
+
|
|
34
|
+
## 市场与 Git 内容版本
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
npx --yes @yep-ai/cli@latest login --registry https://yep.nihao.cool
|
|
38
|
+
npx --yes @yep-ai/cli@latest whoami
|
|
39
|
+
npx --yes @yep-ai/cli@latest search assistant
|
|
40
|
+
npx --yes @yep-ai/cli@latest info @creator/demo
|
|
41
|
+
npx --yes @yep-ai/cli@latest client set codex,claude
|
|
42
|
+
npx --yes @yep-ai/cli@latest install @creator/demo --client codex
|
|
43
|
+
npx --yes @yep-ai/cli@latest list
|
|
44
|
+
npx --yes @yep-ai/cli@latest list --remote
|
|
45
|
+
npx --yes @yep-ai/cli@latest list --mine
|
|
46
|
+
npx --yes @yep-ai/cli@latest uninstall @creator/demo --client codex
|
|
47
|
+
npx --yes @yep-ai/cli@latest publish ./my-skill
|
|
48
|
+
npx --yes @yep-ai/cli@latest history @creator/demo
|
|
49
|
+
npx --yes @yep-ai/cli@latest diff @creator/demo --base published
|
|
50
|
+
npx --yes @yep-ai/cli@latest publish ./my-agent --kind agent
|
|
51
|
+
npx --yes @yep-ai/cli@latest submit @creator/demo --kind agent
|
|
52
|
+
npx --yes @yep-ai/cli@latest withdraw @creator/demo --kind agent
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
默认资源类型为 `skill`,也支持 `--kind agent|plugin`。Skill 私有保存后从网页申请共享审核;Agent/Plugin 的发布会提交审核。审核通过才进入公开市场。`history` 和 `diff` 访问服务端保留的真实 Git 版本。
|
|
56
|
+
|
|
57
|
+
支持的客户端:Codex、Claude、Qoder、Kiro、VS Code、universal 和当前项目;`--target` 显式指定目录。`--local` 可使用源码仓库自带的本地 registry,该选项不访问默认线上市场。安装包继续接受 `cpm.json` 技能清单,并读取历史安装回执。不会执行安装包里的脚本。
|
|
58
|
+
|
|
59
|
+
## 设备节点
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
npx --yes @yep-ai/cli@latest device login --server https://yep.nihao.cool
|
|
63
|
+
npx --yes @yep-ai/cli@latest device start "/path/to/documents" --server https://yep.nihao.cool
|
|
64
|
+
npx --yes @yep-ai/cli@latest device status
|
|
65
|
+
npx --yes @yep-ai/cli@latest device logout
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
设备通过 HTTPS 配对和 WSS 出站连接;网页批准设备后,还需在某场咨询中选择目录和权限。目录不会整体上传。最多指定 5 个目录;指定目录时仅提供受限内置命令。不指定目录时,命令拥有当前系统用户的 shell 权限,可访问其他文件和网络。进程退出后节点离线,不安装后台服务。设备授权最长 24 小时,可以从网页或 CLI 撤销。
|
|
69
|
+
|
|
70
|
+
节点上线后,可在「设备节点」点击「打开终端」,直接选择启动目录并发送命令,无需创建咨询。终端实时展示标准输出、错误输出和退出码,可以停止命令、清屏和重连。`cd 子目录`、`cd ..` 与 `cd` 可切换工作目录;每条命令独立执行,shell 变量不会跨命令保留。CLI 终端不提供交互式输入,`vim`、`top`、口令提示等需要真实终端的程序请使用 SSH 网页终端。单条命令最长运行 30 秒、输出最多 64 KiB;关闭终端、撤销授权或断线会停止正在执行的命令。
|
|
71
|
+
|
|
72
|
+
指定启动目录时仅支持 `pwd`、`ls` / `dir`、`echo`、`cat` / `type`、`head`、`tail`、`grep` / `findstr`、`wc -l` 与终端内的 `cd`,不能使用管道、重定向或外部程序。已有旧版节点若提示不支持终端,请停止旧进程,将原启动命令中的 `yep` 替换为 `npx --yes @yep-ai/cli@latest`,保留原目录参数与 `--server` 地址。
|
|
73
|
+
|
|
74
|
+
`--server` 只接受 HTTPS 源站(回环开发地址允许 HTTP),显式指定后记住该源站;令牌按源站隔离。市场登录不等于设备授权。
|
|
75
|
+
|
|
76
|
+
## 配置
|
|
77
|
+
|
|
78
|
+
默认市场地址:`https://yep.nihao.cool`。
|
|
79
|
+
|
|
80
|
+
| 配置 | 用途 |
|
|
81
|
+
| --- | --- |
|
|
82
|
+
| `YEP_REGISTRY_URL` | 市场 API 源站 |
|
|
83
|
+
| `YEP_TOKEN` | 市场 Bearer token |
|
|
84
|
+
| `YEP_USERNAME` / `YEP_PASSWORD` | 非交互市场登录 |
|
|
85
|
+
| `YEP_CLIENT` | 默认安装客户端 |
|
|
86
|
+
| `YEP_CONFIG_DIR` | 配置根目录,默认 `~/.config/yep` |
|
|
87
|
+
| `YEP_HOME` | 覆盖默认 home,配置位于其 `.config/yep` 下 |
|
|
88
|
+
| `YEP_DEVICE_CONFIG_DIR` | 单独覆盖设备凭据目录 |
|
|
89
|
+
|
|
90
|
+
市场凭据保存为 `~/.config/yep/config.json`;设备身份和各源站的授权保存在 `~/.config/yep/devices/`。目录权限 `0700`、凭据文件 `0600`;Windows 使用当前用户专属 ACL。市场和设备令牌分别管理。域名改变后需要在新源站重新登录、配对,不会把旧源站令牌发送到新地址。
|
|
91
|
+
|
|
92
|
+
执行 `npx --yes @yep-ai/cli@latest --help` 或 `npx --yes @yep-ai/cli@latest device --help` 查看全部选项。
|
package/bin/clients.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join, resolve } from "node:path";
|
|
4
|
+
import { createInterface } from "node:readline/promises";
|
|
5
|
+
|
|
6
|
+
const aliases = {
|
|
7
|
+
"claude-code": "claude", cluade: "claude", "codex-cli": "codex", chatgpt: "codex",
|
|
8
|
+
"github-copilot": "vscode", "kiro-cli": "kiro", qorder: "qoder", agents: "universal"
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export function installClientDefinitions() {
|
|
12
|
+
const home = homedir();
|
|
13
|
+
const codexHome = process.env.CODEX_HOME?.trim() || join(home, ".codex");
|
|
14
|
+
const claudeHome = process.env.CLAUDE_CONFIG_DIR?.trim() || join(home, ".claude");
|
|
15
|
+
return {
|
|
16
|
+
codex: { label: "Codex / ChatGPT", root: resolve(codexHome, "skills"), detect: [codexHome, "/etc/codex"] },
|
|
17
|
+
claude: { label: "Claude Code", root: resolve(claudeHome, "skills"), detect: [claudeHome] },
|
|
18
|
+
qoder: { label: "Qoder", root: join(home, ".qoder", "skills"), detect: [join(home, ".qoder")] },
|
|
19
|
+
kiro: { label: "Kiro CLI", root: join(home, ".kiro", "skills"), detect: [join(home, ".kiro")] },
|
|
20
|
+
vscode: { label: "VS Code / Copilot", root: join(home, ".copilot", "skills"), detect: [join(home, ".copilot")] },
|
|
21
|
+
universal: { label: "Universal agents", root: join(home, ".config", "agents", "skills"), detect: [join(home, ".config", "agents")] },
|
|
22
|
+
project: { label: "Project agents", root: join(process.cwd(), ".agents", "skills"), detect: [] }
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function normalizeClients(value, definitions = installClientDefinitions()) {
|
|
27
|
+
const clients = String(value).split(",").map((item) => item.trim().toLowerCase()).map((item) => aliases[item] ?? item);
|
|
28
|
+
if (clients.some((client) => !client)) throw new Error("--client requires at least one client; remove empty selections");
|
|
29
|
+
for (const client of clients) {
|
|
30
|
+
if (!Object.hasOwn(definitions, client) && !["auto", "all"].includes(client)) {
|
|
31
|
+
throw new Error(`Unknown client: ${client}. Supported clients: auto, all, ${Object.keys(definitions).join(", ")}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return [...new Set(clients)];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function resolveClients(value, definitions = installClientDefinitions()) {
|
|
38
|
+
const detected = Object.keys(definitions).filter((client) => definitions[client].detect.some((path) => existsSync(path)));
|
|
39
|
+
return [...new Set(normalizeClients(value, definitions).flatMap((client) => {
|
|
40
|
+
if (client === "all") return Object.keys(definitions);
|
|
41
|
+
if (client === "auto") return detected.length ? detected : ["project"];
|
|
42
|
+
return [client];
|
|
43
|
+
}))];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function promptClients({ input = process.stdin, output = process.stdout, definitions = installClientDefinitions() } = {}) {
|
|
47
|
+
const clients = Object.keys(definitions);
|
|
48
|
+
output.write("Choose clients (comma-separated numbers or names; 'all' selects every client):\n");
|
|
49
|
+
clients.forEach((client, index) => {
|
|
50
|
+
const definition = definitions[client];
|
|
51
|
+
const detected = definition.detect.some((path) => existsSync(path)) ? " [detected]" : "";
|
|
52
|
+
output.write(` ${index + 1}. ${definition.label} (${client})${detected}\n ${definition.root}\n`);
|
|
53
|
+
});
|
|
54
|
+
const prompt = createInterface({ input, output, terminal: Boolean(input.isTTY && output.isTTY) });
|
|
55
|
+
const abort = new AbortController();
|
|
56
|
+
const cancel = () => abort.abort();
|
|
57
|
+
prompt.on("SIGINT", cancel);
|
|
58
|
+
prompt.on("close", cancel);
|
|
59
|
+
try {
|
|
60
|
+
while (true) {
|
|
61
|
+
const answer = (await prompt.question("Clients: ", { signal: abort.signal })).trim();
|
|
62
|
+
if (["q", "quit", "cancel"].includes(answer.toLowerCase())) throw new Error("Client selection cancelled");
|
|
63
|
+
const values = answer.split(/[\s,]+/).map((item) => /^\d+$/.test(item) ? clients[Number(item) - 1] ?? item : item);
|
|
64
|
+
try {
|
|
65
|
+
return resolveClients(values.join(","), definitions);
|
|
66
|
+
} catch (error) {
|
|
67
|
+
output.write(`${error.message}\n`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
} catch (error) {
|
|
71
|
+
if (abort.signal.aborted) throw new Error("Client selection cancelled");
|
|
72
|
+
throw error;
|
|
73
|
+
} finally {
|
|
74
|
+
prompt.off("close", cancel);
|
|
75
|
+
prompt.close();
|
|
76
|
+
}
|
|
77
|
+
}
|