@yep-ai/cli 0.0.0-stage → 1.0.0
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 +88 -2
- package/bin/clients.js +77 -0
- package/bin/market.js +1621 -0
- package/bin/yep.js +11 -0
- package/lib/device/client.js +411 -0
- package/lib/device/command.js +93 -0
- package/lib/device/tools.js +260 -0
- package/package.json +30 -4
package/README.md
CHANGED
|
@@ -1,3 +1,89 @@
|
|
|
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
|
+
npm install -g @yep-ai/cli
|
|
9
|
+
yep --help
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
也可以无需全局安装直接运行:
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
npx --yes @yep-ai/cli --help
|
|
16
|
+
npx --yes @yep-ai/cli device start --server https://yep.nihao.cool
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
本包包含完整实现。`yep-cli` 是计划中的同版本入口包,当前尚未发布本项目版本,请使用 `@yep-ai/cli`。
|
|
20
|
+
|
|
21
|
+
## 开发与本地安装
|
|
22
|
+
|
|
23
|
+
仓库根目录执行:
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
npm install
|
|
27
|
+
npm run yep -- --help
|
|
28
|
+
npm run pack:cli
|
|
29
|
+
npm install --prefix ./tmp/yep-install ./dist/cli/scoped/yep-ai-cli-1.0.0.tgz ./dist/cli/unscoped/yep-cli-1.0.0.tgz
|
|
30
|
+
./tmp/yep-install/node_modules/.bin/yep --version
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
完整实现包为 `scoped/yep-ai-cli-1.0.0.tgz`,入口包为 `unscoped/yep-cli-1.0.0.tgz`。本地验证时可一起指定两份文件,无需从 npm 下载实现包。输出的 `manifest.json` 记录包名、版本、依赖和 SHA-256。
|
|
34
|
+
|
|
35
|
+
## 市场与 Git 内容版本
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
yep login --registry https://yep.nihao.cool
|
|
39
|
+
yep whoami
|
|
40
|
+
yep search assistant
|
|
41
|
+
yep info @creator/demo
|
|
42
|
+
yep client set codex,claude
|
|
43
|
+
yep install @creator/demo --client codex
|
|
44
|
+
yep list
|
|
45
|
+
yep list --remote
|
|
46
|
+
yep list --mine
|
|
47
|
+
yep uninstall @creator/demo --client codex
|
|
48
|
+
yep publish ./my-skill
|
|
49
|
+
yep history @creator/demo
|
|
50
|
+
yep diff @creator/demo --base published
|
|
51
|
+
yep publish ./my-agent --kind agent
|
|
52
|
+
yep submit @creator/demo --kind agent
|
|
53
|
+
yep withdraw @creator/demo --kind agent
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
默认资源类型为 `skill`,也支持 `--kind agent|plugin`。Skill 私有保存后从网页申请共享审核;Agent/Plugin 的发布会提交审核。审核通过才进入公开市场。`history` 和 `diff` 访问服务端保留的真实 Git 版本。
|
|
57
|
+
|
|
58
|
+
支持的客户端:Codex、Claude、Qoder、Kiro、VS Code、universal 和当前项目;`--target` 显式指定目录。`--local` 可使用源码仓库自带的本地 registry,该选项不访问默认线上市场。安装包继续接受 `cpm.json` 技能清单,并读取历史安装回执。不会执行安装包里的脚本。
|
|
59
|
+
|
|
60
|
+
## 设备节点
|
|
61
|
+
|
|
62
|
+
```sh
|
|
63
|
+
yep device login --server https://yep.nihao.cool
|
|
64
|
+
yep device start "/path/to/documents" --server https://yep.nihao.cool
|
|
65
|
+
yep device status
|
|
66
|
+
yep device logout
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
设备通过 HTTPS 配对和 WSS 出站连接;网页批准设备后,还需在某场咨询中选择目录和权限。目录不会整体上传。最多指定 5 个目录;指定目录时仅提供受限内置命令。不指定目录时,命令拥有当前系统用户的 shell 权限,可访问其他文件和网络。进程退出后节点离线,不安装后台服务。设备授权最长 24 小时,可以从网页或 CLI 撤销。
|
|
70
|
+
|
|
71
|
+
`--server` 只接受 HTTPS 源站(回环开发地址允许 HTTP),显式指定后记住该源站;令牌按源站隔离。市场登录不等于设备授权。
|
|
72
|
+
|
|
73
|
+
## 配置
|
|
74
|
+
|
|
75
|
+
默认市场地址:`https://yep.nihao.cool`。
|
|
76
|
+
|
|
77
|
+
| 配置 | 用途 |
|
|
78
|
+
| --- | --- |
|
|
79
|
+
| `YEP_REGISTRY_URL` | 市场 API 源站 |
|
|
80
|
+
| `YEP_TOKEN` | 市场 Bearer token |
|
|
81
|
+
| `YEP_USERNAME` / `YEP_PASSWORD` | 非交互市场登录 |
|
|
82
|
+
| `YEP_CLIENT` | 默认安装客户端 |
|
|
83
|
+
| `YEP_CONFIG_DIR` | 配置根目录,默认 `~/.config/yep` |
|
|
84
|
+
| `YEP_HOME` | 覆盖默认 home,配置位于其 `.config/yep` 下 |
|
|
85
|
+
| `YEP_DEVICE_CONFIG_DIR` | 单独覆盖设备凭据目录 |
|
|
86
|
+
|
|
87
|
+
市场凭据保存为 `~/.config/yep/config.json`;设备身份和各源站的授权保存在 `~/.config/yep/devices/`。目录权限 `0700`、凭据文件 `0600`;Windows 使用当前用户专属 ACL。市场和设备令牌分别管理。域名改变后需要在新源站重新登录、配对,不会把旧源站令牌发送到新地址。
|
|
88
|
+
|
|
89
|
+
执行 `yep --help` 或 `yep 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
|
+
}
|