easyclaw-link 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 ADDED
@@ -0,0 +1,78 @@
1
+ # easyclaw-cli
2
+
3
+ EasyClaw 平台命令行工具,用于发布和管理技能。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ npm install -g easyclaw
9
+ ```
10
+
11
+ 或直接使用 npx:
12
+
13
+ ```bash
14
+ npx easyclaw <command>
15
+ ```
16
+
17
+ ## 命令
18
+
19
+ ### login
20
+
21
+ 登录 EasyClaw 平台,输入 API Key 并验证保存。
22
+
23
+ ```bash
24
+ npx easyclaw login
25
+ ```
26
+
27
+ API Key 以 `eck_` 开头,保存在 `~/.easyclaw/config.json`。
28
+
29
+ ### publish
30
+
31
+ 发布或更新技能。需要目录中包含 `SKILL.md` 文件。
32
+
33
+ ```bash
34
+ # 发布当前目录
35
+ npx easyclaw publish
36
+
37
+ # 发布指定目录
38
+ npx easyclaw publish ./my-skill
39
+
40
+ # 更新已有技能
41
+ npx easyclaw publish --id <skill-id>
42
+ ```
43
+
44
+ - `SKILL.md`(必须)— 技能内容
45
+ - `package.json`(可选)— 自动提取 name / version / description
46
+
47
+ ### list
48
+
49
+ 查看已发布的所有技能。
50
+
51
+ ```bash
52
+ npx easyclaw list
53
+ ```
54
+
55
+ 输出包含 ID、Title、Status、Calls 的表格。
56
+
57
+ ## 平台 API
58
+
59
+ - Base URL: `https://easyclaw.link`
60
+ - 鉴权: `Authorization: Bearer eck_xxx`
61
+
62
+ | 操作 | 方法 | 路径 |
63
+ | ---------- | ------- | -------------------- |
64
+ | 查询我的技能 | GET | /api/assets/mine |
65
+ | 发布新技能 | POST | /api/assets |
66
+ | 更新技能 | PATCH | /api/assets/:id |
67
+
68
+ ## 开发
69
+
70
+ ```bash
71
+ npm install
72
+ npm run build
73
+ node dist/index.js login
74
+ ```
75
+
76
+ ## License
77
+
78
+ MIT
@@ -0,0 +1,2 @@
1
+ export declare function listAction(): Promise<void>;
2
+ //# sourceMappingURL=list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"AASA,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAwDhD"}
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.listAction = listAction;
4
+ const config_1 = require("../config");
5
+ async function listAction() {
6
+ const apiKey = (0, config_1.requireApiKey)();
7
+ console.log("⏳ 获取技能列表...\n");
8
+ const res = await fetch(`${config_1.BASE_URL}/api/assets?author=me`, {
9
+ headers: { Authorization: `Bearer ${apiKey}` },
10
+ });
11
+ if (!res.ok) {
12
+ const body = await res.text();
13
+ console.error(`❌ 获取失败 (${res.status}): ${body}`);
14
+ process.exit(1);
15
+ }
16
+ const assets = (await res.json());
17
+ if (!assets || assets.length === 0) {
18
+ console.log("📭 暂无技能,使用 npx easyclaw publish 发布你的第一个技能");
19
+ return;
20
+ }
21
+ // Print table header
22
+ const idWidth = 24;
23
+ const titleWidth = 30;
24
+ const statusWidth = 10;
25
+ const callsWidth = 8;
26
+ const header = [
27
+ "ID".padEnd(idWidth),
28
+ "Title".padEnd(titleWidth),
29
+ "Status".padEnd(statusWidth),
30
+ "Calls".padEnd(callsWidth),
31
+ ].join(" | ");
32
+ const separator = [
33
+ "-".repeat(idWidth),
34
+ "-".repeat(titleWidth),
35
+ "-".repeat(statusWidth),
36
+ "-".repeat(callsWidth),
37
+ ].join("-+-");
38
+ console.log(header);
39
+ console.log(separator);
40
+ for (const asset of assets) {
41
+ const row = [
42
+ (asset.id || "").padEnd(idWidth),
43
+ (asset.title || "").slice(0, titleWidth).padEnd(titleWidth),
44
+ (asset.status || "").padEnd(statusWidth),
45
+ String(asset.calls ?? 0).padEnd(callsWidth),
46
+ ].join(" | ");
47
+ console.log(row);
48
+ }
49
+ console.log(`\n共 ${assets.length} 个技能`);
50
+ }
51
+ //# sourceMappingURL=list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":";;AASA,gCAwDC;AAjED,sCAAoD;AAS7C,KAAK,UAAU,UAAU;IAC9B,MAAM,MAAM,GAAG,IAAA,sBAAa,GAAE,CAAC;IAE/B,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IAE7B,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,iBAAQ,uBAAuB,EAAE;QAC1D,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,MAAM,EAAE,EAAE;KAC/C,CAAC,CAAC;IAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC9B,OAAO,CAAC,KAAK,CAAC,WAAW,GAAG,CAAC,MAAM,MAAM,IAAI,EAAE,CAAC,CAAC;QACjD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,MAAM,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAY,CAAC;IAE7C,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnC,OAAO,CAAC,GAAG,CAAC,2CAA2C,CAAC,CAAC;QACzD,OAAO;IACT,CAAC;IAED,qBAAqB;IACrB,MAAM,OAAO,GAAG,EAAE,CAAC;IACnB,MAAM,UAAU,GAAG,EAAE,CAAC;IACtB,MAAM,WAAW,GAAG,EAAE,CAAC;IACvB,MAAM,UAAU,GAAG,CAAC,CAAC;IAErB,MAAM,MAAM,GAAG;QACb,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC;QACpB,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC;QAC1B,QAAQ,CAAC,MAAM,CAAC,WAAW,CAAC;QAC5B,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC;KAC3B,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAEd,MAAM,SAAS,GAAG;QAChB,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC;QACnB,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC;QACtB,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC;QACvB,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC;KACvB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAEd,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACpB,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAEvB,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,GAAG,GAAG;YACV,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;YAChC,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC;YAC3D,CAAC,KAAK,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC;YACxC,MAAM,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC;SAC5C,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACd,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACnB,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,OAAO,MAAM,CAAC,MAAM,MAAM,CAAC,CAAC;AAC1C,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function loginAction(): Promise<void>;
2
+ //# sourceMappingURL=login.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AA2BA,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAyBjD"}
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.loginAction = loginAction;
37
+ const readline = __importStar(require("readline"));
38
+ const config_1 = require("../config");
39
+ function prompt(question) {
40
+ const rl = readline.createInterface({
41
+ input: process.stdin,
42
+ output: process.stdout,
43
+ });
44
+ return new Promise((resolve) => {
45
+ rl.question(question, (answer) => {
46
+ rl.close();
47
+ resolve(answer.trim());
48
+ });
49
+ });
50
+ }
51
+ async function verifyApiKey(apiKey) {
52
+ try {
53
+ const res = await fetch(`${config_1.BASE_URL}/api/assets?author=me`, {
54
+ headers: { Authorization: `Bearer ${apiKey}` },
55
+ });
56
+ return res.ok;
57
+ }
58
+ catch {
59
+ return false;
60
+ }
61
+ }
62
+ async function loginAction() {
63
+ console.log("🔑 登录 EasyClaw 平台\n");
64
+ const apiKey = await prompt("请输入 API Key (eck_xxx): ");
65
+ if (!apiKey) {
66
+ console.error("❌ API Key 不能为空");
67
+ process.exit(1);
68
+ }
69
+ if (!apiKey.startsWith("eck_")) {
70
+ console.error("❌ API Key 格式不正确,应以 eck_ 开头");
71
+ process.exit(1);
72
+ }
73
+ console.log("⏳ 验证 API Key...");
74
+ const valid = await verifyApiKey(apiKey);
75
+ if (!valid) {
76
+ console.error("❌ API Key 无效,请检查后重试");
77
+ process.exit(1);
78
+ }
79
+ (0, config_1.writeConfig)({ apiKey });
80
+ console.log("✅ 登录成功!API Key 已保存到 ~/.easyclaw/config.json");
81
+ }
82
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BA,kCAyBC;AApDD,mDAAqC;AACrC,sCAAkD;AAElD,SAAS,MAAM,CAAC,QAAgB;IAC9B,MAAM,EAAE,GAAG,QAAQ,CAAC,eAAe,CAAC;QAClC,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,MAAM,EAAE,OAAO,CAAC,MAAM;KACvB,CAAC,CAAC;IACH,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE;YAC/B,EAAE,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QACzB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,KAAK,UAAU,YAAY,CAAC,MAAc;IACxC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,iBAAQ,uBAAuB,EAAE;YAC1D,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,MAAM,EAAE,EAAE;SAC/C,CAAC,CAAC;QACH,OAAO,GAAG,CAAC,EAAE,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAEM,KAAK,UAAU,WAAW;IAC/B,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;IAEnC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,yBAAyB,CAAC,CAAC;IAEvD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;QAChC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QAC/B,OAAO,CAAC,KAAK,CAAC,4BAA4B,CAAC,CAAC;QAC5C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IAE/B,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;IACzC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;QACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAA,oBAAW,EAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IACxB,OAAO,CAAC,GAAG,CAAC,6CAA6C,CAAC,CAAC;AAC7D,CAAC"}
@@ -0,0 +1,6 @@
1
+ interface PublishOptions {
2
+ id?: string;
3
+ }
4
+ export declare function publishAction(dir: string | undefined, options: PublishOptions): Promise<void>;
5
+ export {};
6
+ //# sourceMappingURL=publish.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publish.d.ts","sourceRoot":"","sources":["../../src/commands/publish.ts"],"names":[],"mappings":"AAIA,UAAU,cAAc;IACtB,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,GAAG,SAAS,EACvB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,IAAI,CAAC,CAsFf"}
@@ -0,0 +1,118 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.publishAction = publishAction;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const config_1 = require("../config");
40
+ async function publishAction(dir, options) {
41
+ const apiKey = (0, config_1.requireApiKey)();
42
+ const targetDir = path.resolve(dir || ".");
43
+ // Read SKILL.md (required)
44
+ const skillMdPath = path.join(targetDir, "SKILL.md");
45
+ if (!fs.existsSync(skillMdPath)) {
46
+ console.error(`❌ 未找到 ${skillMdPath}`);
47
+ console.error(" publish 需要目录中包含 SKILL.md 文件");
48
+ process.exit(1);
49
+ }
50
+ const content = fs.readFileSync(skillMdPath, "utf-8");
51
+ // Read package.json (optional)
52
+ let pkgName;
53
+ let pkgVersion;
54
+ let pkgDescription;
55
+ const pkgJsonPath = path.join(targetDir, "package.json");
56
+ if (fs.existsSync(pkgJsonPath)) {
57
+ try {
58
+ const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
59
+ pkgName = pkg.name;
60
+ pkgVersion = pkg.version;
61
+ pkgDescription = pkg.description;
62
+ }
63
+ catch {
64
+ // ignore malformed package.json
65
+ }
66
+ }
67
+ const title = pkgName || path.basename(targetDir);
68
+ const description = pkgDescription || "";
69
+ if (options.id) {
70
+ // Update existing skill
71
+ console.log(`⏳ 更新技能 ${options.id}...`);
72
+ const res = await fetch(`${config_1.BASE_URL}/api/assets/${options.id}`, {
73
+ method: "PATCH",
74
+ headers: {
75
+ Authorization: `Bearer ${apiKey}`,
76
+ "Content-Type": "application/json",
77
+ },
78
+ body: JSON.stringify({ content }),
79
+ });
80
+ if (!res.ok) {
81
+ const body = await res.text();
82
+ console.error(`❌ 更新失败 (${res.status}): ${body}`);
83
+ process.exit(1);
84
+ }
85
+ const data = (await res.json());
86
+ console.log(`✅ 技能已更新`);
87
+ console.log(`🔗 ${config_1.BASE_URL}/market/${data.id || options.id}`);
88
+ }
89
+ else {
90
+ // Publish new skill
91
+ console.log(`⏳ 发布新技能: ${title}...`);
92
+ const res = await fetch(`${config_1.BASE_URL}/api/assets`, {
93
+ method: "POST",
94
+ headers: {
95
+ Authorization: `Bearer ${apiKey}`,
96
+ "Content-Type": "application/json",
97
+ },
98
+ body: JSON.stringify({
99
+ title,
100
+ content,
101
+ description: description || title,
102
+ category: "other",
103
+ }),
104
+ });
105
+ if (!res.ok) {
106
+ const body = await res.text();
107
+ console.error(`❌ 发布失败 (${res.status}): ${body}`);
108
+ process.exit(1);
109
+ }
110
+ const data = (await res.json());
111
+ console.log(`✅ 技能发布成功!`);
112
+ console.log(`🔗 ${config_1.BASE_URL}/market/${data.id}`);
113
+ if (pkgVersion) {
114
+ console.log(`📦 版本: ${pkgVersion}`);
115
+ }
116
+ }
117
+ }
118
+ //# sourceMappingURL=publish.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publish.js","sourceRoot":"","sources":["../../src/commands/publish.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export interface Config {
2
+ apiKey?: string;
3
+ }
4
+ export declare function readConfig(): Config;
5
+ export declare function writeConfig(config: Config): void;
6
+ export declare function getApiKey(): string | null;
7
+ export declare function requireApiKey(): string;
8
+ export declare const BASE_URL = "https://easyclaw.link";
9
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,MAAM;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,UAAU,IAAI,MAAM,CAUnC;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAKhD;AAED,wBAAgB,SAAS,IAAI,MAAM,GAAG,IAAI,CAGzC;AAED,wBAAgB,aAAa,IAAI,MAAM,CAOtC;AAED,eAAO,MAAM,QAAQ,0BAA0B,CAAC"}
package/dist/config.js ADDED
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.BASE_URL = void 0;
37
+ exports.readConfig = readConfig;
38
+ exports.writeConfig = writeConfig;
39
+ exports.getApiKey = getApiKey;
40
+ exports.requireApiKey = requireApiKey;
41
+ const fs = __importStar(require("fs"));
42
+ const path = __importStar(require("path"));
43
+ const os = __importStar(require("os"));
44
+ const CONFIG_DIR = path.join(os.homedir(), ".easyclaw");
45
+ const CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
46
+ function readConfig() {
47
+ try {
48
+ if (!fs.existsSync(CONFIG_FILE)) {
49
+ return {};
50
+ }
51
+ const raw = fs.readFileSync(CONFIG_FILE, "utf-8");
52
+ return JSON.parse(raw);
53
+ }
54
+ catch {
55
+ return {};
56
+ }
57
+ }
58
+ function writeConfig(config) {
59
+ if (!fs.existsSync(CONFIG_DIR)) {
60
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
61
+ }
62
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), "utf-8");
63
+ }
64
+ function getApiKey() {
65
+ const config = readConfig();
66
+ return config.apiKey ?? null;
67
+ }
68
+ function requireApiKey() {
69
+ const key = getApiKey();
70
+ if (!key) {
71
+ console.error("❌ 未登录,请先运行 npx easyclaw login");
72
+ process.exit(1);
73
+ }
74
+ return key;
75
+ }
76
+ exports.BASE_URL = "https://easyclaw.link";
77
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAWA,gCAUC;AAED,kCAKC;AAED,8BAGC;AAED,sCAOC;AA1CD,uCAAyB;AACzB,2CAA6B;AAC7B,uCAAyB;AAEzB,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,CAAC,CAAC;AACxD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;AAMzD,SAAgB,UAAU;IACxB,IAAI,CAAC;QACH,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;YAChC,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QAClD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAW,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,SAAgB,WAAW,CAAC,MAAc;IACxC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,CAAC;IACD,EAAE,CAAC,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;AAC1E,CAAC;AAED,SAAgB,SAAS;IACvB,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,OAAO,MAAM,CAAC,MAAM,IAAI,IAAI,CAAC;AAC/B,CAAC;AAED,SAAgB,aAAa;IAC3B,MAAM,GAAG,GAAG,SAAS,EAAE,CAAC;IACxB,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;QAC/C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAEY,QAAA,QAAQ,GAAG,uBAAuB,CAAC"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const commander_1 = require("commander");
5
+ const login_1 = require("./commands/login");
6
+ const publish_1 = require("./commands/publish");
7
+ const list_1 = require("./commands/list");
8
+ const program = new commander_1.Command();
9
+ program
10
+ .name("easyclaw")
11
+ .description("EasyClaw CLI - Publish and manage skills on easyclaw.link")
12
+ .version("1.0.0");
13
+ program
14
+ .command("login")
15
+ .description("登录 EasyClaw 平台,保存 API Key")
16
+ .action(login_1.loginAction);
17
+ program
18
+ .command("publish [dir]")
19
+ .description("发布或更新技能到 EasyClaw 平台")
20
+ .option("--id <id>", "指定技能 ID 进行更新")
21
+ .action(publish_1.publishAction);
22
+ program
23
+ .command("list")
24
+ .description("查看我发布的所有技能")
25
+ .action(list_1.listAction);
26
+ program.parse();
27
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAEA,yCAAoC;AACpC,4CAA+C;AAC/C,gDAAmD;AACnD,0CAA6C;AAE7C,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,UAAU,CAAC;KAChB,WAAW,CAAC,2DAA2D,CAAC;KACxE,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,2BAA2B,CAAC;KACxC,MAAM,CAAC,mBAAW,CAAC,CAAC;AAEvB,OAAO;KACJ,OAAO,CAAC,eAAe,CAAC;KACxB,WAAW,CAAC,sBAAsB,CAAC;KACnC,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC;KACnC,MAAM,CAAC,uBAAa,CAAC,CAAC;AAEzB,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,YAAY,CAAC;KACzB,MAAM,CAAC,iBAAU,CAAC,CAAC;AAEtB,OAAO,CAAC,KAAK,EAAE,CAAC"}
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "easyclaw-link",
3
+ "version": "1.0.0",
4
+ "description": "EasyClaw Link CLI - Publish and manage skills on easyclaw.link",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "easyclaw-link": "dist/index.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "dev": "tsc --watch",
12
+ "prepublishOnly": "npm run build"
13
+ },
14
+ "keywords": [
15
+ "easyclaw",
16
+ "easyclaw-link",
17
+ "cli",
18
+ "skills"
19
+ ],
20
+ "license": "MIT",
21
+ "homepage": "https://easyclaw.link",
22
+ "dependencies": {
23
+ "commander": "^12.1.0",
24
+ "fflate": "^0.8.2"
25
+ },
26
+ "devDependencies": {
27
+ "@types/node": "^20.11.0",
28
+ "typescript": "^5.3.0"
29
+ }
30
+ }
@@ -0,0 +1,66 @@
1
+ import { requireApiKey, BASE_URL } from "../config";
2
+
3
+ interface Asset {
4
+ id: string;
5
+ title: string;
6
+ status: string;
7
+ calls: number;
8
+ }
9
+
10
+ export async function listAction(): Promise<void> {
11
+ const apiKey = requireApiKey();
12
+
13
+ console.log("⏳ 获取技能列表...\n");
14
+
15
+ const res = await fetch(`${BASE_URL}/api/assets?author=me`, {
16
+ headers: { Authorization: `Bearer ${apiKey}` },
17
+ });
18
+
19
+ if (!res.ok) {
20
+ const body = await res.text();
21
+ console.error(`❌ 获取失败 (${res.status}): ${body}`);
22
+ process.exit(1);
23
+ }
24
+
25
+ const assets = (await res.json()) as Asset[];
26
+
27
+ if (!assets || assets.length === 0) {
28
+ console.log("📭 暂无技能,使用 npx easyclaw publish 发布你的第一个技能");
29
+ return;
30
+ }
31
+
32
+ // Print table header
33
+ const idWidth = 24;
34
+ const titleWidth = 30;
35
+ const statusWidth = 10;
36
+ const callsWidth = 8;
37
+
38
+ const header = [
39
+ "ID".padEnd(idWidth),
40
+ "Title".padEnd(titleWidth),
41
+ "Status".padEnd(statusWidth),
42
+ "Calls".padEnd(callsWidth),
43
+ ].join(" | ");
44
+
45
+ const separator = [
46
+ "-".repeat(idWidth),
47
+ "-".repeat(titleWidth),
48
+ "-".repeat(statusWidth),
49
+ "-".repeat(callsWidth),
50
+ ].join("-+-");
51
+
52
+ console.log(header);
53
+ console.log(separator);
54
+
55
+ for (const asset of assets) {
56
+ const row = [
57
+ (asset.id || "").padEnd(idWidth),
58
+ (asset.title || "").slice(0, titleWidth).padEnd(titleWidth),
59
+ (asset.status || "").padEnd(statusWidth),
60
+ String(asset.calls ?? 0).padEnd(callsWidth),
61
+ ].join(" | ");
62
+ console.log(row);
63
+ }
64
+
65
+ console.log(`\n共 ${assets.length} 个技能`);
66
+ }
@@ -0,0 +1,53 @@
1
+ import * as readline from "readline";
2
+ import { writeConfig, BASE_URL } from "../config";
3
+
4
+ function prompt(question: string): Promise<string> {
5
+ const rl = readline.createInterface({
6
+ input: process.stdin,
7
+ output: process.stdout,
8
+ });
9
+ return new Promise((resolve) => {
10
+ rl.question(question, (answer) => {
11
+ rl.close();
12
+ resolve(answer.trim());
13
+ });
14
+ });
15
+ }
16
+
17
+ async function verifyApiKey(apiKey: string): Promise<boolean> {
18
+ try {
19
+ const res = await fetch(`${BASE_URL}/api/assets?author=me`, {
20
+ headers: { Authorization: `Bearer ${apiKey}` },
21
+ });
22
+ return res.ok;
23
+ } catch {
24
+ return false;
25
+ }
26
+ }
27
+
28
+ export async function loginAction(): Promise<void> {
29
+ console.log("🔑 登录 EasyClaw 平台\n");
30
+
31
+ const apiKey = await prompt("请输入 API Key (eck_xxx): ");
32
+
33
+ if (!apiKey) {
34
+ console.error("❌ API Key 不能为空");
35
+ process.exit(1);
36
+ }
37
+
38
+ if (!apiKey.startsWith("eck_")) {
39
+ console.error("❌ API Key 格式不正确,应以 eck_ 开头");
40
+ process.exit(1);
41
+ }
42
+
43
+ console.log("⏳ 验证 API Key...");
44
+
45
+ const valid = await verifyApiKey(apiKey);
46
+ if (!valid) {
47
+ console.error("❌ API Key 无效,请检查后重试");
48
+ process.exit(1);
49
+ }
50
+
51
+ writeConfig({ apiKey });
52
+ console.log("✅ 登录成功!API Key 已保存到 ~/.easyclaw/config.json");
53
+ }
@@ -0,0 +1,98 @@
1
+ import * as fs from "fs";
2
+ import * as path from "path";
3
+ import { requireApiKey, BASE_URL } from "../config";
4
+
5
+ interface PublishOptions {
6
+ id?: string;
7
+ }
8
+
9
+ export async function publishAction(
10
+ dir: string | undefined,
11
+ options: PublishOptions
12
+ ): Promise<void> {
13
+ const apiKey = requireApiKey();
14
+ const targetDir = path.resolve(dir || ".");
15
+
16
+ // Read SKILL.md (required)
17
+ const skillMdPath = path.join(targetDir, "SKILL.md");
18
+ if (!fs.existsSync(skillMdPath)) {
19
+ console.error(`❌ 未找到 ${skillMdPath}`);
20
+ console.error(" publish 需要目录中包含 SKILL.md 文件");
21
+ process.exit(1);
22
+ }
23
+ const content = fs.readFileSync(skillMdPath, "utf-8");
24
+
25
+ // Read package.json (optional)
26
+ let pkgName: string | undefined;
27
+ let pkgVersion: string | undefined;
28
+ let pkgDescription: string | undefined;
29
+
30
+ const pkgJsonPath = path.join(targetDir, "package.json");
31
+ if (fs.existsSync(pkgJsonPath)) {
32
+ try {
33
+ const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
34
+ pkgName = pkg.name;
35
+ pkgVersion = pkg.version;
36
+ pkgDescription = pkg.description;
37
+ } catch {
38
+ // ignore malformed package.json
39
+ }
40
+ }
41
+
42
+ const title = pkgName || path.basename(targetDir);
43
+ const description = pkgDescription || "";
44
+
45
+ if (options.id) {
46
+ // Update existing skill
47
+ console.log(`⏳ 更新技能 ${options.id}...`);
48
+
49
+ const res = await fetch(`${BASE_URL}/api/assets/${options.id}`, {
50
+ method: "PATCH",
51
+ headers: {
52
+ Authorization: `Bearer ${apiKey}`,
53
+ "Content-Type": "application/json",
54
+ },
55
+ body: JSON.stringify({ content }),
56
+ });
57
+
58
+ if (!res.ok) {
59
+ const body = await res.text();
60
+ console.error(`❌ 更新失败 (${res.status}): ${body}`);
61
+ process.exit(1);
62
+ }
63
+
64
+ const data = (await res.json()) as { id: string };
65
+ console.log(`✅ 技能已更新`);
66
+ console.log(`🔗 ${BASE_URL}/market/${data.id || options.id}`);
67
+ } else {
68
+ // Publish new skill
69
+ console.log(`⏳ 发布新技能: ${title}...`);
70
+
71
+ const res = await fetch(`${BASE_URL}/api/assets`, {
72
+ method: "POST",
73
+ headers: {
74
+ Authorization: `Bearer ${apiKey}`,
75
+ "Content-Type": "application/json",
76
+ },
77
+ body: JSON.stringify({
78
+ title,
79
+ content,
80
+ description: description || title,
81
+ category: "other",
82
+ }),
83
+ });
84
+
85
+ if (!res.ok) {
86
+ const body = await res.text();
87
+ console.error(`❌ 发布失败 (${res.status}): ${body}`);
88
+ process.exit(1);
89
+ }
90
+
91
+ const data = (await res.json()) as { id: string };
92
+ console.log(`✅ 技能发布成功!`);
93
+ console.log(`🔗 ${BASE_URL}/market/${data.id}`);
94
+ if (pkgVersion) {
95
+ console.log(`📦 版本: ${pkgVersion}`);
96
+ }
97
+ }
98
+ }
package/src/config.ts ADDED
@@ -0,0 +1,45 @@
1
+ import * as fs from "fs";
2
+ import * as path from "path";
3
+ import * as os from "os";
4
+
5
+ const CONFIG_DIR = path.join(os.homedir(), ".easyclaw");
6
+ const CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
7
+
8
+ export interface Config {
9
+ apiKey?: string;
10
+ }
11
+
12
+ export function readConfig(): Config {
13
+ try {
14
+ if (!fs.existsSync(CONFIG_FILE)) {
15
+ return {};
16
+ }
17
+ const raw = fs.readFileSync(CONFIG_FILE, "utf-8");
18
+ return JSON.parse(raw) as Config;
19
+ } catch {
20
+ return {};
21
+ }
22
+ }
23
+
24
+ export function writeConfig(config: Config): void {
25
+ if (!fs.existsSync(CONFIG_DIR)) {
26
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
27
+ }
28
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), "utf-8");
29
+ }
30
+
31
+ export function getApiKey(): string | null {
32
+ const config = readConfig();
33
+ return config.apiKey ?? null;
34
+ }
35
+
36
+ export function requireApiKey(): string {
37
+ const key = getApiKey();
38
+ if (!key) {
39
+ console.error("❌ 未登录,请先运行 npx easyclaw login");
40
+ process.exit(1);
41
+ }
42
+ return key;
43
+ }
44
+
45
+ export const BASE_URL = "https://easyclaw.link";
package/src/index.ts ADDED
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from "commander";
4
+ import { loginAction } from "./commands/login";
5
+ import { publishAction } from "./commands/publish";
6
+ import { listAction } from "./commands/list";
7
+
8
+ const program = new Command();
9
+
10
+ program
11
+ .name("easyclaw")
12
+ .description("EasyClaw CLI - Publish and manage skills on easyclaw.link")
13
+ .version("1.0.0");
14
+
15
+ program
16
+ .command("login")
17
+ .description("登录 EasyClaw 平台,保存 API Key")
18
+ .action(loginAction);
19
+
20
+ program
21
+ .command("publish [dir]")
22
+ .description("发布或更新技能到 EasyClaw 平台")
23
+ .option("--id <id>", "指定技能 ID 进行更新")
24
+ .action(publishAction);
25
+
26
+ program
27
+ .command("list")
28
+ .description("查看我发布的所有技能")
29
+ .action(listAction);
30
+
31
+ program.parse();
package/tsconfig.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "commonjs",
5
+ "outDir": "dist",
6
+ "rootDir": "src",
7
+ "strict": true,
8
+ "esModuleInterop": true,
9
+ "skipLibCheck": true,
10
+ "forceConsistentCasingInFileNames": true,
11
+ "resolveJsonModule": true,
12
+ "declaration": true,
13
+ "declarationMap": true,
14
+ "sourceMap": true
15
+ },
16
+ "include": ["src/**/*"],
17
+ "exclude": ["node_modules", "dist"]
18
+ }