@skylandnpm/octopus-cli 0.0.1 → 0.0.2
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/dist/commands/attachments.js +6 -3
- package/dist/commands/config.js +118 -23
- package/dist/commands/tasks.js +10 -8
- package/dist/commands/todos.js +4 -4
- package/dist/config.js +46 -5
- package/package.json +1 -1
|
@@ -11,10 +11,11 @@ export function registerAttachmentsCommands(program) {
|
|
|
11
11
|
.description("下载任务附件")
|
|
12
12
|
.requiredOption("--project-no <number>", "项目编号", parseInt)
|
|
13
13
|
.requiredOption("--attachment-id <id>", "附件ID")
|
|
14
|
+
.option("--email <email>", "用户邮箱(用于匹配对应 accessToken,多账号时建议指定)")
|
|
14
15
|
.option("--output <path>", "下载保存路径")
|
|
15
16
|
.action(async (opts) => {
|
|
16
17
|
try {
|
|
17
|
-
const client = getClient();
|
|
18
|
+
const client = getClient(opts.email);
|
|
18
19
|
const res = await client.get(`/api/open/tasks/attachments/${opts.attachmentId}/download?projectNo=${opts.projectNo}`);
|
|
19
20
|
if (res.data.success) {
|
|
20
21
|
const { fileName, fileUrl } = res.data.data;
|
|
@@ -46,9 +47,10 @@ export function registerAttachmentsCommands(program) {
|
|
|
46
47
|
.requiredOption("--log-id <logId>", "流水记录ID")
|
|
47
48
|
.requiredOption("--project-no <number>", "项目编号", parseInt)
|
|
48
49
|
.requiredOption("--file <path>", "附件文件路径")
|
|
50
|
+
.option("--email <email>", "用户邮箱(用于匹配对应 accessToken,多账号时建议指定)")
|
|
49
51
|
.action(async (opts) => {
|
|
50
52
|
try {
|
|
51
|
-
const client = getClient();
|
|
53
|
+
const client = getClient(opts.email);
|
|
52
54
|
const FormData = (await import("form-data")).default;
|
|
53
55
|
const form = new FormData();
|
|
54
56
|
form.append("projectNo", opts.projectNo);
|
|
@@ -76,9 +78,10 @@ export function registerAttachmentsCommands(program) {
|
|
|
76
78
|
.description("获取流水记录附件列表")
|
|
77
79
|
.requiredOption("--log-id <logId>", "流水记录ID")
|
|
78
80
|
.requiredOption("--project-no <number>", "项目编号", parseInt)
|
|
81
|
+
.option("--email <email>", "用户邮箱(用于匹配对应 accessToken,多账号时建议指定)")
|
|
79
82
|
.action(async (opts) => {
|
|
80
83
|
try {
|
|
81
|
-
const client = getClient();
|
|
84
|
+
const client = getClient(opts.email);
|
|
82
85
|
const res = await client.post(`/api/open/task-logs/attachments/${opts.logId}/list`, { projectNo: opts.projectNo });
|
|
83
86
|
if (res.data.success) {
|
|
84
87
|
console.log(JSON.stringify(res.data.data, null, 2));
|
package/dist/commands/config.js
CHANGED
|
@@ -1,54 +1,149 @@
|
|
|
1
1
|
import { loadConfig, saveConfig } from "../config.js";
|
|
2
|
-
const KEY_MAP = {
|
|
3
|
-
baseUrl: "OCTOPUS_BASE_URL",
|
|
4
|
-
token: "OCTOPUS_ACCESS_TOKEN",
|
|
5
|
-
};
|
|
6
2
|
export function registerConfigCommands(program) {
|
|
7
3
|
const configCmd = program.command("config").description("配置管理");
|
|
4
|
+
// ─── config get ───
|
|
8
5
|
configCmd
|
|
9
6
|
.command("get")
|
|
10
7
|
.description("查看当前配置")
|
|
11
8
|
.action(() => {
|
|
12
9
|
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
|
-
: "(未设置)";
|
|
10
|
+
const baseUrl = config.baseUrl || config.env?.OCTOPUS_BASE_URL || "(未设置)";
|
|
17
11
|
console.log(`baseUrl: ${baseUrl}`);
|
|
18
|
-
|
|
12
|
+
if (config.accounts && config.accounts.length > 0) {
|
|
13
|
+
console.log(`\n账号列表 (共 ${config.accounts.length} 个):`);
|
|
14
|
+
for (const acc of config.accounts) {
|
|
15
|
+
const isDefault = acc.email === config.defaultAccount ? " (默认)" : "";
|
|
16
|
+
const masked = "********" + acc.accessToken.slice(-10);
|
|
17
|
+
console.log(` - ${acc.email}${isDefault} token: ${masked}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
const token = config.env?.OCTOPUS_ACCESS_TOKEN
|
|
22
|
+
? "********" + config.env.OCTOPUS_ACCESS_TOKEN.slice(-10)
|
|
23
|
+
: "(未设置)";
|
|
24
|
+
console.log(`token (旧版全局): ${token}`);
|
|
25
|
+
}
|
|
19
26
|
});
|
|
27
|
+
// ─── config set ───
|
|
20
28
|
configCmd
|
|
21
29
|
.command("set <key> <value>")
|
|
22
|
-
.description("设置配置项 (baseUrl
|
|
30
|
+
.description("设置配置项 (baseUrl)")
|
|
23
31
|
.action((key, value) => {
|
|
24
|
-
if (key !== "baseUrl"
|
|
25
|
-
console.log("仅支持设置 baseUrl
|
|
32
|
+
if (key !== "baseUrl") {
|
|
33
|
+
console.log("仅支持设置 baseUrl,账号管理请使用 octopus account 命令");
|
|
26
34
|
return;
|
|
27
35
|
}
|
|
28
36
|
const config = loadConfig();
|
|
37
|
+
config.baseUrl = value.replace(/\/+$/, "");
|
|
38
|
+
// 同步旧版字段
|
|
29
39
|
config.env = config.env || {};
|
|
30
|
-
|
|
31
|
-
config.env[KEY_MAP[key]] =
|
|
32
|
-
finalValue;
|
|
40
|
+
config.env.OCTOPUS_BASE_URL = config.baseUrl;
|
|
33
41
|
saveConfig(config);
|
|
34
42
|
console.log(`已设置 ${key},保存到 ~/.octopus/settings.json`);
|
|
35
43
|
});
|
|
44
|
+
// ─── config delete ───
|
|
36
45
|
configCmd
|
|
37
46
|
.command("delete <key>")
|
|
38
|
-
.description("删除配置项 (baseUrl
|
|
47
|
+
.description("删除配置项 (baseUrl)")
|
|
39
48
|
.action((key) => {
|
|
40
|
-
if (key !== "baseUrl"
|
|
41
|
-
console.log("仅支持删除 baseUrl
|
|
49
|
+
if (key !== "baseUrl") {
|
|
50
|
+
console.log("仅支持删除 baseUrl,账号管理请使用 octopus account 命令");
|
|
42
51
|
return;
|
|
43
52
|
}
|
|
44
53
|
const config = loadConfig();
|
|
45
|
-
|
|
46
|
-
if (
|
|
47
|
-
|
|
54
|
+
delete config.baseUrl;
|
|
55
|
+
if (config.env)
|
|
56
|
+
delete config.env.OCTOPUS_BASE_URL;
|
|
57
|
+
saveConfig(config);
|
|
58
|
+
console.log(`已删除配置项 ${key},保存到 ~/.octopus/settings.json`);
|
|
59
|
+
});
|
|
60
|
+
// ═══════════════════════════════════════
|
|
61
|
+
// account 子命令组
|
|
62
|
+
// ═══════════════════════════════════════
|
|
63
|
+
const accountCmd = program.command("account").description("多账号管理");
|
|
64
|
+
// ─── account add <email> <accessToken> ───
|
|
65
|
+
accountCmd
|
|
66
|
+
.command("add <email> <accessToken>")
|
|
67
|
+
.description("添加账号")
|
|
68
|
+
.action((email, accessToken) => {
|
|
69
|
+
const config = loadConfig();
|
|
70
|
+
config.accounts = config.accounts || [];
|
|
71
|
+
const existing = config.accounts.find((a) => a.email === email);
|
|
72
|
+
if (existing) {
|
|
73
|
+
existing.accessToken = accessToken;
|
|
74
|
+
console.log(`已更新账号 ${email} 的 token`);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
config.accounts.push({ email, accessToken });
|
|
78
|
+
console.log(`已添加账号 ${email}`);
|
|
79
|
+
}
|
|
80
|
+
// 如果只有一个账号,自动设为默认
|
|
81
|
+
if (config.accounts.length === 1) {
|
|
82
|
+
config.defaultAccount = email;
|
|
83
|
+
}
|
|
84
|
+
saveConfig(config);
|
|
85
|
+
});
|
|
86
|
+
// ─── account list ───
|
|
87
|
+
accountCmd
|
|
88
|
+
.command("list")
|
|
89
|
+
.description("查看所有已配置的账号")
|
|
90
|
+
.action(() => {
|
|
91
|
+
const config = loadConfig();
|
|
92
|
+
if (!config.accounts || config.accounts.length === 0) {
|
|
93
|
+
console.log("暂无账号配置,请使用 octopus account add <email> <accessToken> 添加");
|
|
48
94
|
return;
|
|
49
95
|
}
|
|
50
|
-
|
|
96
|
+
console.log(`已配置 ${config.accounts.length} 个账号:\n`);
|
|
97
|
+
for (const acc of config.accounts) {
|
|
98
|
+
const isDefault = acc.email === config.defaultAccount ? " ★ 默认" : "";
|
|
99
|
+
const masked = "********" + acc.accessToken.slice(-10);
|
|
100
|
+
console.log(` ${acc.email}${isDefault}`);
|
|
101
|
+
console.log(` token: ${masked}\n`);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
// ─── account remove <email> ───
|
|
105
|
+
accountCmd
|
|
106
|
+
.command("remove <email>")
|
|
107
|
+
.description("移除指定账号")
|
|
108
|
+
.action((email) => {
|
|
109
|
+
const config = loadConfig();
|
|
110
|
+
if (!config.accounts || config.accounts.length === 0) {
|
|
111
|
+
console.log("暂无账号配置");
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const idx = config.accounts.findIndex((a) => a.email === email);
|
|
115
|
+
if (idx === -1) {
|
|
116
|
+
console.log(`未找到账号 ${email}`);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
config.accounts.splice(idx, 1);
|
|
120
|
+
console.log(`已移除账号 ${email}`);
|
|
121
|
+
// 如果移除的是默认账号,自动切换到第一个
|
|
122
|
+
if (config.defaultAccount === email) {
|
|
123
|
+
config.defaultAccount = config.accounts.length > 0 ? config.accounts[0].email : undefined;
|
|
124
|
+
if (config.defaultAccount) {
|
|
125
|
+
console.log(`默认账号已切换为 ${config.defaultAccount}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
51
128
|
saveConfig(config);
|
|
52
|
-
|
|
129
|
+
});
|
|
130
|
+
// ─── account set-default <email> ───
|
|
131
|
+
accountCmd
|
|
132
|
+
.command("set-default <email>")
|
|
133
|
+
.description("设置默认账号")
|
|
134
|
+
.action((email) => {
|
|
135
|
+
const config = loadConfig();
|
|
136
|
+
if (!config.accounts || config.accounts.length === 0) {
|
|
137
|
+
console.log("暂无账号配置,请先使用 octopus account add 添加");
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const acc = config.accounts.find((a) => a.email === email);
|
|
141
|
+
if (!acc) {
|
|
142
|
+
console.log(`未找到账号 ${email},请先使用 octopus account add 添加`);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
config.defaultAccount = email;
|
|
146
|
+
saveConfig(config);
|
|
147
|
+
console.log(`已将 ${email} 设为默认账号`);
|
|
53
148
|
});
|
|
54
149
|
}
|
package/dist/commands/tasks.js
CHANGED
|
@@ -10,7 +10,7 @@ export function registerTasksCommands(program) {
|
|
|
10
10
|
.option("--status <status>", "任务状态筛选: not_started, in_progress, completed, error, closed")
|
|
11
11
|
.action(async (opts) => {
|
|
12
12
|
try {
|
|
13
|
-
const client = getClient();
|
|
13
|
+
const client = getClient(opts.email);
|
|
14
14
|
const res = await client.post("/api/open/tasks/list", {
|
|
15
15
|
projectNo: opts.projectNo,
|
|
16
16
|
email: opts.email,
|
|
@@ -33,9 +33,10 @@ export function registerTasksCommands(program) {
|
|
|
33
33
|
.description("获取任务详情")
|
|
34
34
|
.requiredOption("--project-no <number>", "项目编号", parseInt)
|
|
35
35
|
.requiredOption("--task-no <number>", "任务编号", parseInt)
|
|
36
|
+
.option("--email <email>", "用户邮箱(用于匹配对应 accessToken,多账号时建议指定)")
|
|
36
37
|
.action(async (opts) => {
|
|
37
38
|
try {
|
|
38
|
-
const client = getClient();
|
|
39
|
+
const client = getClient(opts.email);
|
|
39
40
|
const res = await client.post("/api/open/tasks/detail", {
|
|
40
41
|
projectNo: opts.projectNo,
|
|
41
42
|
taskNo: opts.taskNo,
|
|
@@ -61,7 +62,7 @@ export function registerTasksCommands(program) {
|
|
|
61
62
|
.option("--remark <remark>", "备注信息")
|
|
62
63
|
.action(async (opts) => {
|
|
63
64
|
try {
|
|
64
|
-
const client = getClient();
|
|
65
|
+
const client = getClient(opts.email);
|
|
65
66
|
const res = await client.post("/api/open/tasks/approve", {
|
|
66
67
|
projectNo: opts.projectNo,
|
|
67
68
|
taskNo: opts.taskNo,
|
|
@@ -90,7 +91,7 @@ export function registerTasksCommands(program) {
|
|
|
90
91
|
.option("--remark <remark>", "备注信息")
|
|
91
92
|
.action(async (opts) => {
|
|
92
93
|
try {
|
|
93
|
-
const client = getClient();
|
|
94
|
+
const client = getClient(opts.email);
|
|
94
95
|
const res = await client.post("/api/open/tasks/start", {
|
|
95
96
|
projectNo: opts.projectNo,
|
|
96
97
|
taskNo: opts.taskNo,
|
|
@@ -120,7 +121,7 @@ export function registerTasksCommands(program) {
|
|
|
120
121
|
.option("--hours-spent <minutes>", "本次消耗工时(分钟)", parseInt)
|
|
121
122
|
.action(async (opts) => {
|
|
122
123
|
try {
|
|
123
|
-
const client = getClient();
|
|
124
|
+
const client = getClient(opts.email);
|
|
124
125
|
const res = await client.post("/api/open/tasks/complete", {
|
|
125
126
|
projectNo: opts.projectNo,
|
|
126
127
|
taskNo: opts.taskNo,
|
|
@@ -150,7 +151,7 @@ export function registerTasksCommands(program) {
|
|
|
150
151
|
.requiredOption("--remark <remark>", "异常原因")
|
|
151
152
|
.action(async (opts) => {
|
|
152
153
|
try {
|
|
153
|
-
const client = getClient();
|
|
154
|
+
const client = getClient(opts.email);
|
|
154
155
|
const res = await client.post("/api/open/tasks/error", {
|
|
155
156
|
projectNo: opts.projectNo,
|
|
156
157
|
taskNo: opts.taskNo,
|
|
@@ -179,7 +180,7 @@ export function registerTasksCommands(program) {
|
|
|
179
180
|
.requiredOption("--remark <remark>", "备注内容")
|
|
180
181
|
.action(async (opts) => {
|
|
181
182
|
try {
|
|
182
|
-
const client = getClient();
|
|
183
|
+
const client = getClient(opts.email);
|
|
183
184
|
const res = await client.post("/api/open/tasks/comments", {
|
|
184
185
|
projectNo: opts.projectNo,
|
|
185
186
|
taskNo: opts.taskNo,
|
|
@@ -204,9 +205,10 @@ export function registerTasksCommands(program) {
|
|
|
204
205
|
.description("获取任务附件列表")
|
|
205
206
|
.requiredOption("--project-no <number>", "项目编号", parseInt)
|
|
206
207
|
.requiredOption("--task-no <number>", "任务编号", parseInt)
|
|
208
|
+
.option("--email <email>", "用户邮箱(用于匹配对应 accessToken,多账号时建议指定)")
|
|
207
209
|
.action(async (opts) => {
|
|
208
210
|
try {
|
|
209
|
-
const client = getClient();
|
|
211
|
+
const client = getClient(opts.email);
|
|
210
212
|
const res = await client.post("/api/open/tasks/attachments", {
|
|
211
213
|
projectNo: opts.projectNo,
|
|
212
214
|
taskNo: opts.taskNo,
|
package/dist/commands/todos.js
CHANGED
|
@@ -9,7 +9,7 @@ export function registerTodosCommands(program) {
|
|
|
9
9
|
.requiredOption("--email <email>", "用户邮箱")
|
|
10
10
|
.action(async (opts) => {
|
|
11
11
|
try {
|
|
12
|
-
const client = getClient();
|
|
12
|
+
const client = getClient(opts.email);
|
|
13
13
|
const res = await client.post("/api/open/todos/pending", {
|
|
14
14
|
projectNo: opts.projectNo,
|
|
15
15
|
email: opts.email,
|
|
@@ -34,7 +34,7 @@ export function registerTodosCommands(program) {
|
|
|
34
34
|
.option("--status <status>", "待办状态筛选: pending, approved, completed, failed, suspended, closed")
|
|
35
35
|
.action(async (opts) => {
|
|
36
36
|
try {
|
|
37
|
-
const client = getClient();
|
|
37
|
+
const client = getClient(opts.email);
|
|
38
38
|
const res = await client.post("/api/open/todos/approved", {
|
|
39
39
|
projectNo: opts.projectNo,
|
|
40
40
|
email: opts.email,
|
|
@@ -62,7 +62,7 @@ export function registerTodosCommands(program) {
|
|
|
62
62
|
.option("--description <description>", "待办描述")
|
|
63
63
|
.action(async (opts) => {
|
|
64
64
|
try {
|
|
65
|
-
const client = getClient();
|
|
65
|
+
const client = getClient(opts.email);
|
|
66
66
|
const res = await client.post("/api/open/todos/create", {
|
|
67
67
|
projectNo: opts.projectNo,
|
|
68
68
|
taskNo: opts.taskNo,
|
|
@@ -95,7 +95,7 @@ export function registerTodosCommands(program) {
|
|
|
95
95
|
.option("--tokens-consumed <count>", "消耗Token数量", parseInt)
|
|
96
96
|
.action(async (opts) => {
|
|
97
97
|
try {
|
|
98
|
-
const client = getClient();
|
|
98
|
+
const client = getClient(opts.email);
|
|
99
99
|
const res = await client.post("/api/open/todos/feedback", {
|
|
100
100
|
projectNo: opts.projectNo,
|
|
101
101
|
todoId: opts.todoId,
|
package/dist/config.js
CHANGED
|
@@ -19,14 +19,55 @@ export function saveConfig(config) {
|
|
|
19
19
|
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
20
20
|
writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
21
21
|
}
|
|
22
|
-
|
|
22
|
+
/** 获取 baseUrl,优先从顶层 baseUrl,其次从旧版 env */
|
|
23
|
+
export function getBaseUrl() {
|
|
23
24
|
const config = loadConfig();
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
return config.baseUrl || config.env?.OCTOPUS_BASE_URL || process.env.OCTOPUS_BASE_URL;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* 根据 email 查找对应账号的 accessToken。
|
|
29
|
+
* 如果不传 email,则使用 defaultAccount 或第一个账号。
|
|
30
|
+
*/
|
|
31
|
+
export function resolveToken(email) {
|
|
32
|
+
const config = loadConfig();
|
|
33
|
+
// 新版:从 accounts 中查找
|
|
34
|
+
if (config.accounts && config.accounts.length > 0) {
|
|
35
|
+
let account;
|
|
36
|
+
if (email) {
|
|
37
|
+
account = config.accounts.find((a) => a.email === email);
|
|
38
|
+
if (!account) {
|
|
39
|
+
throw new Error(`未找到邮箱为 ${email} 的账号,请先通过 octopus account add 添加`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
// 使用默认账号
|
|
44
|
+
const defaultEmail = config.defaultAccount || config.accounts[0].email;
|
|
45
|
+
account = config.accounts.find((a) => a.email === defaultEmail);
|
|
46
|
+
if (!account) {
|
|
47
|
+
account = config.accounts[0];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return { token: account.accessToken, resolvedEmail: account.email };
|
|
51
|
+
}
|
|
52
|
+
// 兼容旧版:从全局 token 获取
|
|
53
|
+
const globalToken = config.env?.OCTOPUS_ACCESS_TOKEN || process.env.OCTOPUS_ACCESS_TOKEN;
|
|
54
|
+
if (globalToken) {
|
|
55
|
+
if (!email) {
|
|
56
|
+
throw new Error("旧版全局 token 模式下需要指定 --email 参数,或请迁移到多账号模式 (octopus account add)");
|
|
57
|
+
}
|
|
58
|
+
return { token: globalToken, resolvedEmail: email };
|
|
59
|
+
}
|
|
60
|
+
throw new Error("未配置任何账号,请先运行 octopus account add <email> <accessToken>");
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* 获取 HTTP 客户端。
|
|
64
|
+
* 可选传入 email 参数,用于从 accounts 中匹配对应的 accessToken。
|
|
65
|
+
*/
|
|
66
|
+
export function getClient(email) {
|
|
67
|
+
const baseUrl = getBaseUrl();
|
|
26
68
|
if (!baseUrl)
|
|
27
69
|
throw new Error("缺少 baseUrl,请先运行 octopus config set baseUrl <url>");
|
|
28
|
-
|
|
29
|
-
throw new Error("缺少 token,请先运行 octopus config set token <token>");
|
|
70
|
+
const { token } = resolveToken(email);
|
|
30
71
|
return axios.create({
|
|
31
72
|
baseURL: baseUrl.replace(/\/+$/, ""),
|
|
32
73
|
headers: {
|