@skylandnpm/octopus-cli 0.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/bin/octopus.js ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import "../dist/index.js";
@@ -0,0 +1,94 @@
1
+ import { getClient } from "../config.js";
2
+ import axios from "axios";
3
+ import { createWriteStream, createReadStream } from "fs";
4
+ export function registerAttachmentsCommands(program) {
5
+ const attachmentsCmd = program
6
+ .command("attachments")
7
+ .description("附件相关命令");
8
+ // attachments download
9
+ attachmentsCmd
10
+ .command("download")
11
+ .description("下载任务附件")
12
+ .requiredOption("--project-no <number>", "项目编号", parseInt)
13
+ .requiredOption("--attachment-id <id>", "附件ID")
14
+ .option("--output <path>", "下载保存路径")
15
+ .action(async (opts) => {
16
+ try {
17
+ const client = getClient();
18
+ const res = await client.get(`/api/open/tasks/attachments/${opts.attachmentId}/download?projectNo=${opts.projectNo}`);
19
+ if (res.data.success) {
20
+ const { fileName, fileUrl } = res.data.data;
21
+ const outputPath = opts.output || fileName;
22
+ console.log(`正在下载: ${fileName}`);
23
+ const fileRes = await axios.get(fileUrl, { responseType: "stream" });
24
+ const writer = createWriteStream(outputPath);
25
+ fileRes.data.pipe(writer);
26
+ writer.on("finish", () => {
27
+ console.log(`已保存到: ${outputPath}`);
28
+ });
29
+ writer.on("error", (err) => {
30
+ console.error("保存失败:", err.message);
31
+ });
32
+ }
33
+ else {
34
+ console.error("请求失败:", res.data.message);
35
+ }
36
+ }
37
+ catch (err) {
38
+ console.error("请求失败:", err.response?.data?.message || err.message);
39
+ }
40
+ });
41
+ // logs 子命令
42
+ const logsCmd = program.command("logs").description("任务流水记录附件命令");
43
+ logsCmd
44
+ .command("upload")
45
+ .description("上传流水记录附件")
46
+ .requiredOption("--log-id <logId>", "流水记录ID")
47
+ .requiredOption("--project-no <number>", "项目编号", parseInt)
48
+ .requiredOption("--file <path>", "附件文件路径")
49
+ .action(async (opts) => {
50
+ try {
51
+ const client = getClient();
52
+ const FormData = (await import("form-data")).default;
53
+ const form = new FormData();
54
+ form.append("projectNo", opts.projectNo);
55
+ form.append("file", createReadStream(opts.file));
56
+ const res = await axios.post(`${client.defaults.baseURL}/api/open/task-logs/attachments/${opts.logId}`, form, {
57
+ headers: {
58
+ ...client.defaults.headers.common,
59
+ "Content-Type": "multipart/form-data",
60
+ },
61
+ });
62
+ if (res.data.success) {
63
+ console.log("附件上传成功");
64
+ console.log(JSON.stringify(res.data.data, null, 2));
65
+ }
66
+ else {
67
+ console.error("请求失败:", res.data.message);
68
+ }
69
+ }
70
+ catch (err) {
71
+ console.error("请求失败:", err.response?.data?.message || err.message);
72
+ }
73
+ });
74
+ logsCmd
75
+ .command("attachments")
76
+ .description("获取流水记录附件列表")
77
+ .requiredOption("--log-id <logId>", "流水记录ID")
78
+ .requiredOption("--project-no <number>", "项目编号", parseInt)
79
+ .action(async (opts) => {
80
+ try {
81
+ const client = getClient();
82
+ const res = await client.post(`/api/open/task-logs/attachments/${opts.logId}/list`, { projectNo: opts.projectNo });
83
+ if (res.data.success) {
84
+ console.log(JSON.stringify(res.data.data, null, 2));
85
+ }
86
+ else {
87
+ console.error("请求失败:", res.data.message);
88
+ }
89
+ }
90
+ catch (err) {
91
+ console.error("请求失败:", err.response?.data?.message || err.message);
92
+ }
93
+ });
94
+ }
@@ -0,0 +1,54 @@
1
+ import { loadConfig, saveConfig } from "../config.js";
2
+ const KEY_MAP = {
3
+ baseUrl: "OCTOPUS_BASE_URL",
4
+ token: "OCTOPUS_ACCESS_TOKEN",
5
+ };
6
+ export function registerConfigCommands(program) {
7
+ const configCmd = program.command("config").description("配置管理");
8
+ configCmd
9
+ .command("get")
10
+ .description("查看当前配置")
11
+ .action(() => {
12
+ const config = loadConfig();
13
+ const baseUrl = config.env?.OCTOPUS_BASE_URL || "(未设置)";
14
+ const token = config.env?.OCTOPUS_ACCESS_TOKEN
15
+ ? "********" + config.env.OCTOPUS_ACCESS_TOKEN.slice(-10)
16
+ : "(未设置)";
17
+ console.log(`baseUrl: ${baseUrl}`);
18
+ console.log(`token: ${token}`);
19
+ });
20
+ configCmd
21
+ .command("set <key> <value>")
22
+ .description("设置配置项 (baseUrl | token)")
23
+ .action((key, value) => {
24
+ if (key !== "baseUrl" && key !== "token") {
25
+ console.log("仅支持设置 baseUrl 或 token");
26
+ return;
27
+ }
28
+ const config = loadConfig();
29
+ config.env = config.env || {};
30
+ const finalValue = key === "baseUrl" ? value.replace(/\/+$/, "") : value;
31
+ config.env[KEY_MAP[key]] =
32
+ finalValue;
33
+ saveConfig(config);
34
+ console.log(`已设置 ${key},保存到 ~/.octopus/settings.json`);
35
+ });
36
+ configCmd
37
+ .command("delete <key>")
38
+ .description("删除配置项 (baseUrl | token)")
39
+ .action((key) => {
40
+ if (key !== "baseUrl" && key !== "token") {
41
+ console.log("仅支持删除 baseUrl 或 token");
42
+ return;
43
+ }
44
+ const config = loadConfig();
45
+ const envKey = KEY_MAP[key];
46
+ if (!config.env?.[envKey]) {
47
+ console.log(`配置项 ${key} 不存在`);
48
+ return;
49
+ }
50
+ delete config.env[envKey];
51
+ saveConfig(config);
52
+ console.log(`已删除配置项 ${key},保存到 ~/.octopus/settings.json`);
53
+ });
54
+ }
@@ -0,0 +1,225 @@
1
+ import { getClient } from "../config.js";
2
+ export function registerTasksCommands(program) {
3
+ const tasksCmd = program.command("tasks").description("任务相关命令");
4
+ // tasks list
5
+ tasksCmd
6
+ .command("list")
7
+ .description("获取任务列表")
8
+ .requiredOption("--project-no <number>", "项目编号", parseInt)
9
+ .requiredOption("--email <email>", "用户邮箱")
10
+ .option("--status <status>", "任务状态筛选: not_started, in_progress, completed, error, closed")
11
+ .action(async (opts) => {
12
+ try {
13
+ const client = getClient();
14
+ const res = await client.post("/api/open/tasks/list", {
15
+ projectNo: opts.projectNo,
16
+ email: opts.email,
17
+ status: opts.status,
18
+ });
19
+ if (res.data.success) {
20
+ console.log(JSON.stringify(res.data.data, null, 2));
21
+ }
22
+ else {
23
+ console.error("请求失败:", res.data.message);
24
+ }
25
+ }
26
+ catch (err) {
27
+ console.error("请求失败:", err.response?.data?.message || err.message);
28
+ }
29
+ });
30
+ // tasks detail
31
+ tasksCmd
32
+ .command("detail")
33
+ .description("获取任务详情")
34
+ .requiredOption("--project-no <number>", "项目编号", parseInt)
35
+ .requiredOption("--task-no <number>", "任务编号", parseInt)
36
+ .action(async (opts) => {
37
+ try {
38
+ const client = getClient();
39
+ const res = await client.post("/api/open/tasks/detail", {
40
+ projectNo: opts.projectNo,
41
+ taskNo: opts.taskNo,
42
+ });
43
+ if (res.data.success) {
44
+ console.log(JSON.stringify(res.data.data, null, 2));
45
+ }
46
+ else {
47
+ console.error("请求失败:", res.data.message);
48
+ }
49
+ }
50
+ catch (err) {
51
+ console.error("请求失败:", err.response?.data?.message || err.message);
52
+ }
53
+ });
54
+ // tasks approve
55
+ tasksCmd
56
+ .command("approve")
57
+ .description("批准执行任务")
58
+ .requiredOption("--project-no <number>", "项目编号", parseInt)
59
+ .requiredOption("--task-no <number>", "任务编号", parseInt)
60
+ .requiredOption("--email <email>", "用户邮箱")
61
+ .option("--remark <remark>", "备注信息")
62
+ .action(async (opts) => {
63
+ try {
64
+ const client = getClient();
65
+ const res = await client.post("/api/open/tasks/approve", {
66
+ projectNo: opts.projectNo,
67
+ taskNo: opts.taskNo,
68
+ email: opts.email,
69
+ remark: opts.remark,
70
+ });
71
+ if (res.data.success) {
72
+ console.log("任务已批准执行");
73
+ console.log(JSON.stringify(res.data.data, null, 2));
74
+ }
75
+ else {
76
+ console.error("请求失败:", res.data.message);
77
+ }
78
+ }
79
+ catch (err) {
80
+ console.error("请求失败:", err.response?.data?.message || err.message);
81
+ }
82
+ });
83
+ // tasks start
84
+ tasksCmd
85
+ .command("start")
86
+ .description("开始任务")
87
+ .requiredOption("--project-no <number>", "项目编号", parseInt)
88
+ .requiredOption("--task-no <number>", "任务编号", parseInt)
89
+ .requiredOption("--email <email>", "用户邮箱")
90
+ .option("--remark <remark>", "备注信息")
91
+ .action(async (opts) => {
92
+ try {
93
+ const client = getClient();
94
+ const res = await client.post("/api/open/tasks/start", {
95
+ projectNo: opts.projectNo,
96
+ taskNo: opts.taskNo,
97
+ email: opts.email,
98
+ remark: opts.remark,
99
+ });
100
+ if (res.data.success) {
101
+ console.log("任务已开始");
102
+ console.log(JSON.stringify(res.data.data, null, 2));
103
+ }
104
+ else {
105
+ console.error("请求失败:", res.data.message);
106
+ }
107
+ }
108
+ catch (err) {
109
+ console.error("请求失败:", err.response?.data?.message || err.message);
110
+ }
111
+ });
112
+ // tasks complete
113
+ tasksCmd
114
+ .command("complete")
115
+ .description("完成任务")
116
+ .requiredOption("--project-no <number>", "项目编号", parseInt)
117
+ .requiredOption("--task-no <number>", "任务编号", parseInt)
118
+ .requiredOption("--email <email>", "用户邮箱")
119
+ .option("--remark <remark>", "完成说明")
120
+ .option("--hours-spent <minutes>", "本次消耗工时(分钟)", parseInt)
121
+ .action(async (opts) => {
122
+ try {
123
+ const client = getClient();
124
+ const res = await client.post("/api/open/tasks/complete", {
125
+ projectNo: opts.projectNo,
126
+ taskNo: opts.taskNo,
127
+ email: opts.email,
128
+ remark: opts.remark,
129
+ hoursSpent: opts.hoursSpent,
130
+ });
131
+ if (res.data.success) {
132
+ console.log("任务已完成");
133
+ console.log(JSON.stringify(res.data.data, null, 2));
134
+ }
135
+ else {
136
+ console.error("请求失败:", res.data.message);
137
+ }
138
+ }
139
+ catch (err) {
140
+ console.error("请求失败:", err.response?.data?.message || err.message);
141
+ }
142
+ });
143
+ // tasks error
144
+ tasksCmd
145
+ .command("error")
146
+ .description("标记任务异常")
147
+ .requiredOption("--project-no <number>", "项目编号", parseInt)
148
+ .requiredOption("--task-no <number>", "任务编号", parseInt)
149
+ .requiredOption("--email <email>", "用户邮箱")
150
+ .requiredOption("--remark <remark>", "异常原因")
151
+ .action(async (opts) => {
152
+ try {
153
+ const client = getClient();
154
+ const res = await client.post("/api/open/tasks/error", {
155
+ projectNo: opts.projectNo,
156
+ taskNo: opts.taskNo,
157
+ email: opts.email,
158
+ remark: opts.remark,
159
+ });
160
+ if (res.data.success) {
161
+ console.log("任务已标记异常");
162
+ console.log(JSON.stringify(res.data.data, null, 2));
163
+ }
164
+ else {
165
+ console.error("请求失败:", res.data.message);
166
+ }
167
+ }
168
+ catch (err) {
169
+ console.error("请求失败:", err.response?.data?.message || err.message);
170
+ }
171
+ });
172
+ // tasks comment
173
+ tasksCmd
174
+ .command("comment")
175
+ .description("添加任务备注")
176
+ .requiredOption("--project-no <number>", "项目编号", parseInt)
177
+ .requiredOption("--task-no <number>", "任务编号", parseInt)
178
+ .requiredOption("--email <email>", "用户邮箱")
179
+ .requiredOption("--remark <remark>", "备注内容")
180
+ .action(async (opts) => {
181
+ try {
182
+ const client = getClient();
183
+ const res = await client.post("/api/open/tasks/comments", {
184
+ projectNo: opts.projectNo,
185
+ taskNo: opts.taskNo,
186
+ email: opts.email,
187
+ remark: opts.remark,
188
+ });
189
+ if (res.data.success) {
190
+ console.log("备注添加成功");
191
+ console.log(JSON.stringify(res.data.data, null, 2));
192
+ }
193
+ else {
194
+ console.error("请求失败:", res.data.message);
195
+ }
196
+ }
197
+ catch (err) {
198
+ console.error("请求失败:", err.response?.data?.message || err.message);
199
+ }
200
+ });
201
+ // tasks attachments
202
+ tasksCmd
203
+ .command("attachments")
204
+ .description("获取任务附件列表")
205
+ .requiredOption("--project-no <number>", "项目编号", parseInt)
206
+ .requiredOption("--task-no <number>", "任务编号", parseInt)
207
+ .action(async (opts) => {
208
+ try {
209
+ const client = getClient();
210
+ const res = await client.post("/api/open/tasks/attachments", {
211
+ projectNo: opts.projectNo,
212
+ taskNo: opts.taskNo,
213
+ });
214
+ if (res.data.success) {
215
+ console.log(JSON.stringify(res.data.data, null, 2));
216
+ }
217
+ else {
218
+ console.error("请求失败:", res.data.message);
219
+ }
220
+ }
221
+ catch (err) {
222
+ console.error("请求失败:", err.response?.data?.message || err.message);
223
+ }
224
+ });
225
+ }
@@ -0,0 +1,120 @@
1
+ import { getClient } from "../config.js";
2
+ export function registerTodosCommands(program) {
3
+ const todosCmd = program.command("todos").description("待办事项相关命令");
4
+ // todos pending
5
+ todosCmd
6
+ .command("pending")
7
+ .description("获取待处理待办列表")
8
+ .requiredOption("--project-no <number>", "项目编号", parseInt)
9
+ .requiredOption("--email <email>", "用户邮箱")
10
+ .action(async (opts) => {
11
+ try {
12
+ const client = getClient();
13
+ const res = await client.post("/api/open/todos/pending", {
14
+ projectNo: opts.projectNo,
15
+ email: opts.email,
16
+ });
17
+ if (res.data.success) {
18
+ console.log(JSON.stringify(res.data.data, null, 2));
19
+ }
20
+ else {
21
+ console.error("请求失败:", res.data.message);
22
+ }
23
+ }
24
+ catch (err) {
25
+ console.error("请求失败:", err.response?.data?.message || err.message);
26
+ }
27
+ });
28
+ // todos approved
29
+ todosCmd
30
+ .command("approved")
31
+ .description("获取已批准待办列表")
32
+ .requiredOption("--project-no <number>", "项目编号", parseInt)
33
+ .requiredOption("--email <email>", "用户邮箱")
34
+ .option("--status <status>", "待办状态筛选: pending, approved, completed, failed, suspended, closed")
35
+ .action(async (opts) => {
36
+ try {
37
+ const client = getClient();
38
+ const res = await client.post("/api/open/todos/approved", {
39
+ projectNo: opts.projectNo,
40
+ email: opts.email,
41
+ status: opts.status,
42
+ });
43
+ if (res.data.success) {
44
+ console.log(JSON.stringify(res.data.data, null, 2));
45
+ }
46
+ else {
47
+ console.error("请求失败:", res.data.message);
48
+ }
49
+ }
50
+ catch (err) {
51
+ console.error("请求失败:", err.response?.data?.message || err.message);
52
+ }
53
+ });
54
+ // todos create
55
+ todosCmd
56
+ .command("create")
57
+ .description("创建待办")
58
+ .requiredOption("--project-no <number>", "项目编号", parseInt)
59
+ .requiredOption("--task-no <number>", "任务编号", parseInt)
60
+ .requiredOption("--email <email>", "用户邮箱")
61
+ .requiredOption("--title <title>", "待办标题")
62
+ .option("--description <description>", "待办描述")
63
+ .action(async (opts) => {
64
+ try {
65
+ const client = getClient();
66
+ const res = await client.post("/api/open/todos/create", {
67
+ projectNo: opts.projectNo,
68
+ taskNo: opts.taskNo,
69
+ email: opts.email,
70
+ title: opts.title,
71
+ description: opts.description,
72
+ });
73
+ if (res.data.success) {
74
+ console.log("待办创建成功");
75
+ console.log(JSON.stringify(res.data.data, null, 2));
76
+ }
77
+ else {
78
+ console.error("请求失败:", res.data.message);
79
+ }
80
+ }
81
+ catch (err) {
82
+ console.error("请求失败:", err.response?.data?.message || err.message);
83
+ }
84
+ });
85
+ // todos feedback
86
+ todosCmd
87
+ .command("feedback")
88
+ .description("待办反馈(状态变更)")
89
+ .requiredOption("--project-no <number>", "项目编号", parseInt)
90
+ .requiredOption("--todo-id <todoId>", "待办事项ID")
91
+ .requiredOption("--email <email>", "用户邮箱")
92
+ .requiredOption("--status <status>", "目标状态: completed, failed, suspended")
93
+ .option("--feedback <feedback>", "处理结果反馈内容")
94
+ .option("--processing-time <minutes>", "处理用时(分钟)", parseInt)
95
+ .option("--tokens-consumed <count>", "消耗Token数量", parseInt)
96
+ .action(async (opts) => {
97
+ try {
98
+ const client = getClient();
99
+ const res = await client.post("/api/open/todos/feedback", {
100
+ projectNo: opts.projectNo,
101
+ todoId: opts.todoId,
102
+ email: opts.email,
103
+ status: opts.status,
104
+ feedback: opts.feedback,
105
+ processingTime: opts.processingTime,
106
+ tokensConsumed: opts.tokensConsumed,
107
+ });
108
+ if (res.data.success) {
109
+ console.log("待办状态更新成功");
110
+ console.log(JSON.stringify(res.data.data, null, 2));
111
+ }
112
+ else {
113
+ console.error("请求失败:", res.data.message);
114
+ }
115
+ }
116
+ catch (err) {
117
+ console.error("请求失败:", err.response?.data?.message || err.message);
118
+ }
119
+ });
120
+ }
package/dist/config.js ADDED
@@ -0,0 +1,37 @@
1
+ import axios from "axios";
2
+ import { homedir } from "os";
3
+ import { join } from "path";
4
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
5
+ const CONFIG_DIR = join(homedir(), ".octopus");
6
+ const CONFIG_FILE = join(CONFIG_DIR, "settings.json");
7
+ export function loadConfig() {
8
+ if (!existsSync(CONFIG_FILE))
9
+ return {};
10
+ try {
11
+ return JSON.parse(readFileSync(CONFIG_FILE, "utf-8"));
12
+ }
13
+ catch {
14
+ return {};
15
+ }
16
+ }
17
+ export function saveConfig(config) {
18
+ if (!existsSync(CONFIG_DIR))
19
+ mkdirSync(CONFIG_DIR, { recursive: true });
20
+ writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
21
+ }
22
+ export function getClient() {
23
+ const config = loadConfig();
24
+ const baseUrl = config.env?.OCTOPUS_BASE_URL || process.env.OCTOPUS_BASE_URL;
25
+ const token = config.env?.OCTOPUS_ACCESS_TOKEN || process.env.OCTOPUS_ACCESS_TOKEN;
26
+ if (!baseUrl)
27
+ throw new Error("缺少 baseUrl,请先运行 octopus config set baseUrl <url>");
28
+ if (!token)
29
+ throw new Error("缺少 token,请先运行 octopus config set token <token>");
30
+ return axios.create({
31
+ baseURL: baseUrl.replace(/\/+$/, ""),
32
+ headers: {
33
+ "X-Access-Token": token,
34
+ "Content-Type": "application/json",
35
+ },
36
+ });
37
+ }
package/dist/index.js ADDED
@@ -0,0 +1,13 @@
1
+ import { Command } from "commander";
2
+ import { registerConfigCommands } from "./commands/config.js";
3
+ import { registerTasksCommands } from "./commands/tasks.js";
4
+ import { registerTodosCommands } from "./commands/todos.js";
5
+ import { registerAttachmentsCommands } from "./commands/attachments.js";
6
+ import pkg from "../package.json" with { type: "json" };
7
+ const program = new Command();
8
+ program.name("octopus").description("Octopus CLI").version(pkg.version);
9
+ registerConfigCommands(program);
10
+ registerTasksCommands(program);
11
+ registerTodosCommands(program);
12
+ registerAttachmentsCommands(program);
13
+ program.parse();
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@skylandnpm/octopus-cli",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "bin": {
6
+ "octopus": "./bin/octopus.js"
7
+ },
8
+ "scripts": {
9
+ "build": "tsc",
10
+ "build:patch:publish": "pnpm build && npm version patch && npm publish",
11
+ "dev": "tsc -w"
12
+ },
13
+ "files": [
14
+ "bin",
15
+ "dist"
16
+ ],
17
+ "dependencies": {
18
+ "axios": "^1.7.9",
19
+ "commander": "^12.1.0",
20
+ "form-data": "^4.0.5"
21
+ },
22
+ "devDependencies": {
23
+ "@types/node": "^22.10.2",
24
+ "vite": "8.0.3"
25
+ },
26
+ "peerDependencies": {
27
+ "typescript": "^5"
28
+ },
29
+ "publishConfig": {
30
+ "access": "public",
31
+ "registry": "https://registry.npmjs.org"
32
+ }
33
+ }