@yep-ai/cli 1.0.2 → 1.0.3
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 +30 -28
- package/lib/device/client.js +25 -2
- package/lib/device/tools.js +32 -0
- package/native/darwin-arm64/yep-node +0 -0
- package/native/darwin-x64/yep-node +0 -0
- package/native/linux-arm64/yep-node +0 -0
- package/native/linux-x64/yep-node +0 -0
- package/native/manifest.json +7 -7
- package/native/win32-arm64/yep-node.exe +0 -0
- package/native/win32-x64/yep-node.exe +0 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
## 安装与使用
|
|
6
6
|
|
|
7
7
|
```sh
|
|
8
|
-
npx --yes
|
|
8
|
+
npx --yes --package=https://yep.nihao.cool/downloads/yep-cli-1.0.3.tgz yep --help
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
通过 npx
|
|
11
|
+
通过 npx 直接运行本站已发布的设备节点安装包,无需全局安装:
|
|
12
12
|
|
|
13
13
|
```sh
|
|
14
|
-
npx --yes
|
|
15
|
-
npx --yes
|
|
14
|
+
npx --yes --package=https://yep.nihao.cool/downloads/yep-cli-1.0.3.tgz yep --help
|
|
15
|
+
npx --yes --package=https://yep.nihao.cool/downloads/yep-cli-1.0.3.tgz yep device start
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
本包包含完整实现。`yep-cli` 是计划中的同版本入口包,当前尚未发布本项目版本,请使用 `@yep-ai/cli`。
|
|
@@ -26,31 +26,31 @@ npm install
|
|
|
26
26
|
npm run yep -- --help
|
|
27
27
|
npm run build:yep-node
|
|
28
28
|
npm run pack:cli
|
|
29
|
-
npm install --prefix ./tmp/yep-install ./dist/cli/scoped/yep-ai-cli-1.0.
|
|
29
|
+
npm install --prefix ./tmp/yep-install ./dist/cli/scoped/yep-ai-cli-1.0.3.tgz ./dist/cli/unscoped/yep-cli-1.0.3.tgz
|
|
30
30
|
./tmp/yep-install/node_modules/.bin/yep --version
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
完整实现包为 `scoped/yep-ai-cli-1.0.
|
|
33
|
+
完整实现包为 `scoped/yep-ai-cli-1.0.3.tgz`,入口包为 `unscoped/yep-cli-1.0.3.tgz`。本地验证时可一起指定两份文件,无需从 npm 下载实现包。输出的 `manifest.json` 记录包名、版本、依赖和 SHA-256。
|
|
34
34
|
|
|
35
35
|
## 市场与 Git 内容版本
|
|
36
36
|
|
|
37
37
|
```sh
|
|
38
|
-
npx --yes
|
|
39
|
-
npx --yes
|
|
40
|
-
npx --yes
|
|
41
|
-
npx --yes
|
|
42
|
-
npx --yes
|
|
43
|
-
npx --yes
|
|
44
|
-
npx --yes
|
|
45
|
-
npx --yes
|
|
46
|
-
npx --yes
|
|
47
|
-
npx --yes
|
|
48
|
-
npx --yes
|
|
49
|
-
npx --yes
|
|
50
|
-
npx --yes
|
|
51
|
-
npx --yes
|
|
52
|
-
npx --yes
|
|
53
|
-
npx --yes
|
|
38
|
+
npx --yes --package=https://yep.nihao.cool/downloads/yep-cli-1.0.3.tgz yep login --registry https://yep.nihao.cool
|
|
39
|
+
npx --yes --package=https://yep.nihao.cool/downloads/yep-cli-1.0.3.tgz yep whoami
|
|
40
|
+
npx --yes --package=https://yep.nihao.cool/downloads/yep-cli-1.0.3.tgz yep search assistant
|
|
41
|
+
npx --yes --package=https://yep.nihao.cool/downloads/yep-cli-1.0.3.tgz yep info @creator/demo
|
|
42
|
+
npx --yes --package=https://yep.nihao.cool/downloads/yep-cli-1.0.3.tgz yep client set codex,claude
|
|
43
|
+
npx --yes --package=https://yep.nihao.cool/downloads/yep-cli-1.0.3.tgz yep install @creator/demo --client codex
|
|
44
|
+
npx --yes --package=https://yep.nihao.cool/downloads/yep-cli-1.0.3.tgz yep list
|
|
45
|
+
npx --yes --package=https://yep.nihao.cool/downloads/yep-cli-1.0.3.tgz yep list --remote
|
|
46
|
+
npx --yes --package=https://yep.nihao.cool/downloads/yep-cli-1.0.3.tgz yep list --mine
|
|
47
|
+
npx --yes --package=https://yep.nihao.cool/downloads/yep-cli-1.0.3.tgz yep uninstall @creator/demo --client codex
|
|
48
|
+
npx --yes --package=https://yep.nihao.cool/downloads/yep-cli-1.0.3.tgz yep publish ./my-skill
|
|
49
|
+
npx --yes --package=https://yep.nihao.cool/downloads/yep-cli-1.0.3.tgz yep history @creator/demo
|
|
50
|
+
npx --yes --package=https://yep.nihao.cool/downloads/yep-cli-1.0.3.tgz yep diff @creator/demo --base published
|
|
51
|
+
npx --yes --package=https://yep.nihao.cool/downloads/yep-cli-1.0.3.tgz yep publish ./my-agent --kind agent
|
|
52
|
+
npx --yes --package=https://yep.nihao.cool/downloads/yep-cli-1.0.3.tgz yep submit @creator/demo --kind agent
|
|
53
|
+
npx --yes --package=https://yep.nihao.cool/downloads/yep-cli-1.0.3.tgz yep withdraw @creator/demo --kind agent
|
|
54
54
|
```
|
|
55
55
|
|
|
56
56
|
默认资源类型为 `skill`,也支持 `--kind agent|plugin`。Skill 私有保存后从网页申请共享审核;Agent/Plugin 的发布会提交审核。审核通过才进入公开市场。`history` 和 `diff` 访问服务端保留的真实 Git 版本。
|
|
@@ -60,20 +60,22 @@ npx --yes @yep-ai/cli@latest withdraw @creator/demo --kind agent
|
|
|
60
60
|
## 设备节点
|
|
61
61
|
|
|
62
62
|
```sh
|
|
63
|
-
npx --yes
|
|
64
|
-
npx --yes
|
|
65
|
-
npx --yes
|
|
66
|
-
npx --yes
|
|
63
|
+
npx --yes --package=https://yep.nihao.cool/downloads/yep-cli-1.0.3.tgz yep device login
|
|
64
|
+
npx --yes --package=https://yep.nihao.cool/downloads/yep-cli-1.0.3.tgz yep device start "/path/to/documents"
|
|
65
|
+
npx --yes --package=https://yep.nihao.cool/downloads/yep-cli-1.0.3.tgz yep device status
|
|
66
|
+
npx --yes --package=https://yep.nihao.cool/downloads/yep-cli-1.0.3.tgz yep device logout
|
|
67
67
|
```
|
|
68
68
|
|
|
69
69
|
设备通过 HTTPS 配对和 WSS 出站连接;网页批准设备后,还需在某场咨询中选择目录和权限。目录不会整体上传。最多指定 5 个目录;指定目录时仅提供受限内置命令。不指定目录时,命令拥有当前系统用户的 shell 权限,可访问其他文件和网络。进程退出后节点离线,不安装后台服务。设备授权最长 24 小时,可以从网页或 CLI 撤销。
|
|
70
70
|
|
|
71
71
|
节点上线后,可在「设备节点」点击「打开终端」。不指定启动目录时,npm 包内的 Go `yep-node` 为每个标签启动独立的真实 Shell:Windows 使用 ConPTY + cmd,macOS / Linux 使用 PTY + 当前用户的 Shell。输入直接进入终端,工作目录、变量和交互程序状态保留;支持 Ctrl+C、窗口尺寸变化和最多 4 个独立网页会话。AI 操作与网页终端分别执行,不共用工作目录或进程。关闭标签、撤销授权、节点退出或网络连接断开会终止对应 Shell;重连创建新会话,不重放命令。
|
|
72
72
|
|
|
73
|
+
终端中的「+」可选择文件浏览器,Windows 还可选择 CMD 或 PowerShell。标签显示实际 Shell 名称,同名时才编号;仅当前标签显示关闭按钮。文件浏览器显示设备上的完整路径,选中文件可在底部展开大小、修改时间等概要,通过自定义右键菜单下载文件或复制完整路径。受限模式只能访问已授权目录,并继续排除 CLI 私有凭据及符号链接;下载期间文件变化、设备断线或授权失效会中断下载。
|
|
74
|
+
|
|
73
75
|
原生程序随 npm 包一起分发,无需用户安装 Go。当前构建目标为 Windows x64 / ARM64、macOS Intel / Apple Silicon、Linux x64 / ARM64;Windows 需要支持 ConPTY 的 Windows 10 1809 / Server 2019 或更新版本。鸿蒙 / OpenHarmony 原生 ABI 尚未适配,不能将 Linux ARM64 包视为鸿蒙包。缺少原生程序时会明确提示并提供兼容的逐条命令模式;受限目录始终使用内置命令模式,不启动系统 Shell。
|
|
74
76
|
|
|
75
77
|
设备配对和网络通道使用 HTTPS / WSS;服务端保存令牌摘要,客户端每小时轮换设备令牌并保留短暂交接窗口,轮换不会延长原有 24 小时授权。客户端先持久化待交接凭据,断线后继续完成交接。TLS 在站点入口终止,集群内部连接不属于端到端加密。
|
|
76
|
-
指定启动目录时仅支持 `pwd`、`ls` / `dir`、`echo`、`cat` / `type`、`head`、`tail`、`grep` / `findstr`、`wc -l` 与终端内的 `cd`,不能使用管道、重定向或外部程序。已有旧版节点若提示不支持终端,请停止旧进程,将原启动命令中的 `yep` 替换为 `npx --yes
|
|
78
|
+
指定启动目录时仅支持 `pwd`、`ls` / `dir`、`echo`、`cat` / `type`、`head`、`tail`、`grep` / `findstr`、`wc -l` 与终端内的 `cd`,不能使用管道、重定向或外部程序。已有旧版节点若提示不支持终端,请停止旧进程,将原启动命令中的 `yep` 替换为 `npx --yes --package=https://yep.nihao.cool/downloads/yep-cli-1.0.3.tgz yep`,保留原目录参数。
|
|
77
79
|
|
|
78
80
|
设备默认连接 `https://yep.nihao.cool`,使用本站时无需指定服务器。连接自建站点时可使用 `--server https://your-site.example`;只接受 HTTPS 源站(回环开发地址允许 HTTP),显式指定后记住该源站,令牌按源站隔离。市场登录不等于设备授权。
|
|
79
81
|
|
|
@@ -93,4 +95,4 @@ npx --yes @yep-ai/cli@latest device logout
|
|
|
93
95
|
|
|
94
96
|
市场凭据保存为 `~/.config/yep/config.json`;设备身份和各源站的授权保存在 `~/.config/yep/devices/`。目录权限 `0700`、凭据文件 `0600`;Windows 使用当前用户专属 ACL。市场和设备令牌分别管理。域名改变后需要在新源站重新登录、配对,不会把旧源站令牌发送到新地址。
|
|
95
97
|
|
|
96
|
-
执行 `npx --yes
|
|
98
|
+
执行 `npx --yes --package=https://yep.nihao.cool/downloads/yep-cli-1.0.3.tgz yep --help` 或 `npx --yes --package=https://yep.nihao.cool/downloads/yep-cli-1.0.3.tgz yep device --help` 查看全部选项。
|
package/lib/device/client.js
CHANGED
|
@@ -4,7 +4,7 @@ import os from "node:os";
|
|
|
4
4
|
import { createHash, randomUUID } from "node:crypto";
|
|
5
5
|
import { spawn } from "node:child_process";
|
|
6
6
|
import WebSocket from "ws";
|
|
7
|
-
import { rootDescriptor, resolveSafe, list, execute, terminalDirectory } from "./tools.js";
|
|
7
|
+
import { rootDescriptor, resolveSafe, list, execute, terminalDirectory, ownerFileList, ownerFileRead } from "./tools.js";
|
|
8
8
|
import { findNativeBinary, NativeTerminal } from "./native-terminal.js";
|
|
9
9
|
import { rotateDeviceCredential } from "./credential-rotation.js";
|
|
10
10
|
const PROTOCOL = 2;
|
|
@@ -297,6 +297,29 @@ class DeviceAgent {
|
|
|
297
297
|
throw new Error("Device authorization expired");
|
|
298
298
|
}
|
|
299
299
|
if (["terminal_exec", "terminal_cancel", "terminal_close"].includes(frame.type)) return this.terminalFrame(frame);
|
|
300
|
+
if (["owner_file_list", "owner_file_read"].includes(frame.type)) {
|
|
301
|
+
if (typeof frame.id !== "string" || !frame.id || this.active.has(frame.id) || this.active.size >= 8) throw new Error("Too many or duplicate local operations");
|
|
302
|
+
let root = this.roots.find(item => item.id === frame.folderId) ?? (!frame.folderId ? this.roots[0] : undefined);
|
|
303
|
+
let relative = frame.path ?? "";
|
|
304
|
+
if (frame.absolutePath) {
|
|
305
|
+
if (typeof frame.absolutePath !== "string" || frame.absolutePath.length > 4096 || !path.isAbsolute(frame.absolutePath) || /[\0\r\n]/u.test(frame.absolutePath)) throw new Error("Invalid absolute path");
|
|
306
|
+
const location = path.resolve(frame.absolutePath);
|
|
307
|
+
const matches = this.roots.filter(item => { const rel = path.relative(item.absolute, location); return rel === "" || rel !== ".." && !rel.startsWith(".." + path.sep) && !path.isAbsolute(rel); }).sort((a, b) => b.absolute.length - a.absolute.length);
|
|
308
|
+
root = matches[0];
|
|
309
|
+
if (!root) throw new Error("Path is outside authorized directories");
|
|
310
|
+
relative = path.relative(root.absolute, location).split(path.sep).join("/");
|
|
311
|
+
}
|
|
312
|
+
if (!root) throw new Error("Unknown file root");
|
|
313
|
+
const abort = new AbortController();
|
|
314
|
+
let complete;
|
|
315
|
+
const done = new Promise(resolve => { complete = resolve; });
|
|
316
|
+
this.active.set(frame.id, { abort, done });
|
|
317
|
+
try {
|
|
318
|
+
const result = frame.type === "owner_file_list" ? await ownerFileList(root, relative, frame.offset ?? 0) : await ownerFileRead(root, relative, frame.offset ?? 0, frame.version ?? "");
|
|
319
|
+
if (this.closed || abort.signal.aborted) throw new Error("Operation canceled");
|
|
320
|
+
return { type: "result", id: frame.id, ...result, ...(frame.type === "owner_file_list" ? { folders: this.roots.map(({ id, name, absolute }) => ({ id, name, absolutePath: absolute })) } : {}) };
|
|
321
|
+
} finally { this.active.delete(frame.id); complete(); }
|
|
322
|
+
}
|
|
300
323
|
const base = { id: frame.id, consultationId: frame.consultationId, bindingId: frame.bindingId, roundId: frame.roundId };
|
|
301
324
|
if (frame.type === "cancel") {
|
|
302
325
|
await this.cancel(frame);
|
|
@@ -477,7 +500,7 @@ async function start(server, config, credential, directories, { name, noOpen, si
|
|
|
477
500
|
signal?.addEventListener("abort", abort, { once: true });
|
|
478
501
|
ws.on("open", () => {
|
|
479
502
|
received();
|
|
480
|
-
ws.send(JSON.stringify({ type: "hello", protocol: PROTOCOL, consentVersion: "yep-device-v2", directoryMode: restricted ? "restricted" : "system", folders: agent.publicRoots(), commandsEnabled: true, fileSelection: true, capabilities: ["terminal", ...(native ? ["pty"] : [])] }));
|
|
503
|
+
ws.send(JSON.stringify({ type: "hello", protocol: PROTOCOL, consentVersion: "yep-device-v2", directoryMode: restricted ? "restricted" : "system", folders: agent.publicRoots(), commandsEnabled: true, fileSelection: true, capabilities: ["terminal", "files-v1", ...(native ? ["pty", "shell-selection"] : [])] }));
|
|
481
504
|
});
|
|
482
505
|
ws.on("ping", received);
|
|
483
506
|
ws.on("pong", received);
|
package/lib/device/tools.js
CHANGED
|
@@ -105,6 +105,36 @@ async function list(root, relative = "", offset = 0, includeFiles = true) {
|
|
|
105
105
|
}
|
|
106
106
|
return { folderId: root.id, path: relative, parentPath: relative ? relative.split("/").slice(0, -1).join("/") : null, entries: page, offset, hasMore: offset + 200 < filtered.length };
|
|
107
107
|
}
|
|
108
|
+
// Owner-only browser operations. Absolute paths are returned only on this
|
|
109
|
+
// protocol, never in public roots or AI bindings.
|
|
110
|
+
async function ownerFileList(root, relative = "", offset = 0) {
|
|
111
|
+
const result = await list(root, relative, offset, true);
|
|
112
|
+
const entries = [];
|
|
113
|
+
for (const entry of result.entries) {
|
|
114
|
+
try {
|
|
115
|
+
const { absolute, stat } = await resolveSafe(root, entry.path);
|
|
116
|
+
entries.push({ ...entry, absolutePath: absolute, isDirectory: stat.isDirectory(), size: stat.isFile() ? stat.size : 0, modifiedAt: stat.mtime.toISOString() });
|
|
117
|
+
} catch { /* A removed or inaccessible entry does not break the directory. */ }
|
|
118
|
+
}
|
|
119
|
+
return { ...result, nextOffset: result.hasMore ? offset + 200 : null, absolutePath: (await resolveSafe(root, relative, "directory")).absolute, entries };
|
|
120
|
+
}
|
|
121
|
+
async function ownerFileRead(root, relative, offset = 0, version = "") {
|
|
122
|
+
if (!Number.isSafeInteger(offset) || offset < 0) throw new Error("Invalid file offset");
|
|
123
|
+
const resolved = await resolveSafe(root, relative, "file");
|
|
124
|
+
const fd = await pinnedOpen(root, relative);
|
|
125
|
+
try {
|
|
126
|
+
const before = await fd.stat();
|
|
127
|
+
const identity = `${before.dev}:${before.ino}:${before.size}:${before.mtimeMs}:${before.ctimeMs}`;
|
|
128
|
+
if (before.ino !== resolved.stat.ino || before.dev !== resolved.stat.dev || !before.isFile() || version && version !== identity) throw new Error("File changed during download; please retry");
|
|
129
|
+
if (offset > before.size) throw new Error("Invalid file offset");
|
|
130
|
+
const buffer = Buffer.alloc(Math.min(256 * 1024, before.size - offset));
|
|
131
|
+
const { bytesRead } = await fd.read(buffer, 0, buffer.length, offset);
|
|
132
|
+
const after = await fd.stat();
|
|
133
|
+
if (`${after.dev}:${after.ino}:${after.size}:${after.mtimeMs}:${after.ctimeMs}` !== identity) throw new Error("File changed during download; please retry");
|
|
134
|
+
await resolveSafe(root, relative, "file");
|
|
135
|
+
return { name: path.basename(relative), size: before.size, offset, version: identity, data: buffer.subarray(0, bytesRead).toString("base64"), eof: offset + bytesRead === before.size };
|
|
136
|
+
} finally { await fd.close(); }
|
|
137
|
+
}
|
|
108
138
|
function utf8(data) {
|
|
109
139
|
const text = new TextDecoder("utf-8", { fatal: true }).decode(data);
|
|
110
140
|
if (text.includes("\0")) throw new Error("File is not UTF-8 text");
|
|
@@ -337,6 +367,8 @@ export {
|
|
|
337
367
|
execute,
|
|
338
368
|
fileOperation,
|
|
339
369
|
list,
|
|
370
|
+
ownerFileList,
|
|
371
|
+
ownerFileRead,
|
|
340
372
|
resolveSafe,
|
|
341
373
|
rootDescriptor,
|
|
342
374
|
safeRelative,
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/native/manifest.json
CHANGED
|
@@ -1,34 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": 1,
|
|
3
|
-
"sourceSHA256": "
|
|
3
|
+
"sourceSHA256": "de2d089833202ddf0943a16360b1d3e61229ffb4aa91c82d293d193c68152f55",
|
|
4
4
|
"files": {
|
|
5
5
|
"win32-x64/yep-node.exe": {
|
|
6
|
-
"sha256": "
|
|
6
|
+
"sha256": "4d02c921b40636278e1959e1080503210d5f7622a2bd712b026d1155d0195787",
|
|
7
7
|
"goos": "windows",
|
|
8
8
|
"goarch": "amd64"
|
|
9
9
|
},
|
|
10
10
|
"win32-arm64/yep-node.exe": {
|
|
11
|
-
"sha256": "
|
|
11
|
+
"sha256": "febcb2d56ddac514f16c6aa6d4d272c6113fbb8e82c7e09045fb36c242be9e2a",
|
|
12
12
|
"goos": "windows",
|
|
13
13
|
"goarch": "arm64"
|
|
14
14
|
},
|
|
15
15
|
"darwin-x64/yep-node": {
|
|
16
|
-
"sha256": "
|
|
16
|
+
"sha256": "0ff37c8c6d45f82d1c5f112306ca5b4d2a16054c50b521536bb12c051df02e05",
|
|
17
17
|
"goos": "darwin",
|
|
18
18
|
"goarch": "amd64"
|
|
19
19
|
},
|
|
20
20
|
"darwin-arm64/yep-node": {
|
|
21
|
-
"sha256": "
|
|
21
|
+
"sha256": "d94adea76c11039a60098dadab8b09068df18de1c4024202e24536047a007114",
|
|
22
22
|
"goos": "darwin",
|
|
23
23
|
"goarch": "arm64"
|
|
24
24
|
},
|
|
25
25
|
"linux-x64/yep-node": {
|
|
26
|
-
"sha256": "
|
|
26
|
+
"sha256": "51e6a8173acacc828f4cbc774f026571fb2e7ec5cf6adf7a27581bbe12fd193b",
|
|
27
27
|
"goos": "linux",
|
|
28
28
|
"goarch": "amd64"
|
|
29
29
|
},
|
|
30
30
|
"linux-arm64/yep-node": {
|
|
31
|
-
"sha256": "
|
|
31
|
+
"sha256": "8924a2a3c09dee1e1532c1dad64c32251bace31b6670bd049041153c1614e3c7",
|
|
32
32
|
"goos": "linux",
|
|
33
33
|
"goarch": "arm64"
|
|
34
34
|
}
|
|
Binary file
|
|
Binary file
|