codex-work-receipt 0.7.4 → 0.8.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/CHANGELOG.md +14 -0
- package/README.en.md +27 -7
- package/README.md +27 -7
- package/docs/cli.en.md +55 -5
- package/docs/cli.md +55 -5
- package/docs/codex-pet.en.md +1 -1
- package/docs/codex-pet.md +1 -1
- package/docs/codex-skill.en.md +2 -0
- package/docs/codex-skill.md +2 -0
- package/docs/data-schema.en.md +29 -4
- package/docs/data-schema.md +29 -4
- package/docs/fixtures/cwr-file-v1.json +33 -0
- package/docs/miniprogram-file-import.en.md +67 -0
- package/docs/miniprogram-file-import.md +67 -0
- package/docs/mobile-import.en.md +19 -11
- package/docs/mobile-import.md +18 -10
- package/docs/privacy.en.md +20 -5
- package/docs/privacy.md +20 -5
- package/package.json +2 -2
- package/skills/ai-work-receipt/SKILL.md +2 -2
- package/src/auto-hook.mjs +46 -0
- package/src/auto-runner.mjs +172 -0
- package/src/cli.mjs +158 -60
- package/src/core/args.mjs +34 -0
- package/src/core/auto-mode.mjs +364 -0
- package/src/core/file-payload.mjs +74 -0
- package/src/core/generator.mjs +98 -0
- package/src/core/mode-selector.mjs +31 -0
- package/src/core/presentation.mjs +64 -28
- package/src/core/qr-payload.mjs +7 -178
- package/src/core/receipt-record.mjs +18 -10
- package/src/core/transfer-record.mjs +137 -0
- package/src/lib/files.mjs +52 -0
- package/src/parsers/codex.mjs +6 -6
- package/src/renderers/html.mjs +343 -220
package/src/cli.mjs
CHANGED
|
@@ -1,45 +1,33 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { spawn } from "node:child_process";
|
|
4
|
-
import fs from "node:fs";
|
|
5
4
|
import path from "node:path";
|
|
6
5
|
import { fileURLToPath } from "node:url";
|
|
7
6
|
import QRCode from "qrcode";
|
|
8
7
|
|
|
9
8
|
import { parseArgs, printHelp } from "./core/args.mjs";
|
|
10
|
-
import {
|
|
11
|
-
|
|
9
|
+
import {
|
|
10
|
+
automaticOutputPath,
|
|
11
|
+
configureManualMode,
|
|
12
|
+
enableAutomaticMode,
|
|
13
|
+
getAutomaticStatus,
|
|
14
|
+
getWorkReceiptHome,
|
|
15
|
+
readAutoConfig,
|
|
16
|
+
startAutomaticRun,
|
|
17
|
+
} from "./core/auto-mode.mjs";
|
|
18
|
+
import { generateReceipt } from "./core/generator.mjs";
|
|
19
|
+
import { promptForGenerationMode } from "./core/mode-selector.mjs";
|
|
12
20
|
import { printOpenSourcePrompt } from "./core/open-source.mjs";
|
|
13
21
|
import { getRollingSummaryNotice, getScopeLabel } from "./core/presentation.mjs";
|
|
14
|
-
import {
|
|
15
|
-
import { outputSlugForRange, resolveRange } from "./core/range.mjs";
|
|
16
|
-
import { buildReceiptRecord, persistReceiptRecord } from "./core/receipt-record.mjs";
|
|
22
|
+
import { encodeSingleReceiptQr } from "./core/qr-payload.mjs";
|
|
17
23
|
import { promptForRange } from "./core/selector.mjs";
|
|
18
24
|
import { installCodexSkill } from "./core/skill-installer.mjs";
|
|
19
25
|
import { installCodexPet, uninstallCodexPet } from "./core/pet-installer.mjs";
|
|
20
26
|
import { formatNumber } from "./lib/time.mjs";
|
|
21
|
-
import { listRecentCodexSessions
|
|
22
|
-
import { renderHtml } from "./renderers/html.mjs";
|
|
27
|
+
import { listRecentCodexSessions } from "./parsers/codex.mjs";
|
|
23
28
|
|
|
24
29
|
const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
25
30
|
const PROJECT_DIR = path.dirname(SCRIPT_DIR);
|
|
26
|
-
const DEFAULT_OUTPUT_DIR = path.join(process.cwd(), "codex-work-receipt-output");
|
|
27
|
-
const DEFAULT_MINIPROGRAM_CODE = path.join(PROJECT_DIR, "assets", "miniprogram-code.png");
|
|
28
|
-
|
|
29
|
-
function mimeTypeForImage(filePath) {
|
|
30
|
-
const extension = path.extname(filePath).toLowerCase();
|
|
31
|
-
if (extension === ".png") return "image/png";
|
|
32
|
-
if (extension === ".jpg" || extension === ".jpeg") return "image/jpeg";
|
|
33
|
-
if (extension === ".webp") return "image/webp";
|
|
34
|
-
if (extension === ".svg") return "image/svg+xml";
|
|
35
|
-
throw new Error(`不支持的小程序码图片格式:${extension || "未知"}`);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function imageAsDataUrl(filePath) {
|
|
39
|
-
if (!filePath || !fs.existsSync(filePath)) return null;
|
|
40
|
-
return `data:${mimeTypeForImage(filePath)};base64,${fs.readFileSync(filePath).toString("base64")}`;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
31
|
function openFile(filePath) {
|
|
44
32
|
const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
|
|
45
33
|
const args = process.platform === "win32" ? ["/c", "start", "", filePath] : [filePath];
|
|
@@ -47,12 +35,112 @@ function openFile(filePath) {
|
|
|
47
35
|
child.unref();
|
|
48
36
|
}
|
|
49
37
|
|
|
38
|
+
function isInteractive() {
|
|
39
|
+
return Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function printAutomaticEnabled(result, locale) {
|
|
43
|
+
const outputFile = automaticOutputPath(result.config);
|
|
44
|
+
const importFile = outputFile.replace(/\.html?$/i, ".cwr.json");
|
|
45
|
+
if (locale === "en") {
|
|
46
|
+
console.log("Automatic saving enabled.");
|
|
47
|
+
console.log("Codex will quietly refresh today's receipt and WeChat import file whenever a turn stops.");
|
|
48
|
+
console.log(`Automatic receipt: ${outputFile}`);
|
|
49
|
+
console.log(`WeChat import file: ${importFile}`);
|
|
50
|
+
console.log("Restart Codex. If Codex asks you to review the new hook, open /hooks and trust AI Work Receipt.");
|
|
51
|
+
} else {
|
|
52
|
+
console.log("自动保存已启用。");
|
|
53
|
+
console.log("Codex 每完成一轮工作,都会静默刷新今天的小票和微信导入文件。");
|
|
54
|
+
console.log(`自动小票:${outputFile}`);
|
|
55
|
+
console.log(`微信导入文件:${importFile}`);
|
|
56
|
+
console.log("请重启 Codex;如果 Codex 提示审查新 Hook,请打开 /hooks 并信任 AI 打工小票。");
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function printManualEnabled(result, locale) {
|
|
61
|
+
if (locale === "en") {
|
|
62
|
+
console.log("Manual-only mode enabled. No AI Work Receipt hook will run in the background.");
|
|
63
|
+
if (result.removedHook?.removed) console.log("The AI Work Receipt hook was removed; existing receipts were kept.");
|
|
64
|
+
} else {
|
|
65
|
+
console.log("已切换为仅手动模式,AI 打工小票不会在后台自动运行。");
|
|
66
|
+
if (result.removedHook?.removed) console.log("已移除 AI 打工小票 Hook,历史小票仍然保留。");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function enableAuto(options) {
|
|
71
|
+
const result = enableAutomaticMode({
|
|
72
|
+
projectDir: PROJECT_DIR,
|
|
73
|
+
dataDir: options.dataDir,
|
|
74
|
+
locale: options.locale,
|
|
75
|
+
timezone: options.timezone,
|
|
76
|
+
theme: options.theme,
|
|
77
|
+
});
|
|
78
|
+
startAutomaticRun(result.config);
|
|
79
|
+
printAutomaticEnabled(result, options.locale);
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function enableManual(options) {
|
|
84
|
+
const result = configureManualMode({
|
|
85
|
+
dataDir: options.dataDir,
|
|
86
|
+
locale: options.locale,
|
|
87
|
+
timezone: options.timezone,
|
|
88
|
+
theme: options.theme,
|
|
89
|
+
});
|
|
90
|
+
printManualEnabled(result, options.locale);
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function printAutoStatus(options) {
|
|
95
|
+
const status = getAutomaticStatus({ dataDir: options.dataDir });
|
|
96
|
+
if (options.locale === "en") {
|
|
97
|
+
console.log(`Mode: ${status.mode}`);
|
|
98
|
+
console.log(`Local data: ${status.workReceiptHome}`);
|
|
99
|
+
console.log(`Runtime: ${status.runtimeInstalled ? "installed" : "not installed"}`);
|
|
100
|
+
console.log(`Codex hook: ${status.hookInstalled ? "installed" : "not installed"}`);
|
|
101
|
+
if (status.state?.last_success_at) console.log(`Last automatic receipt: ${status.state.last_success_at}`);
|
|
102
|
+
if (status.state?.import_file) console.log(`WeChat import file: ${status.state.import_file}`);
|
|
103
|
+
if (status.state?.error) console.log(`Last message: ${status.state.error}`);
|
|
104
|
+
} else {
|
|
105
|
+
const modeLabel = status.mode === "automatic" ? "自动保存" : status.mode === "manual" ? "仅手动" : "尚未设置";
|
|
106
|
+
console.log(`模式:${modeLabel}`);
|
|
107
|
+
console.log(`本地数据:${status.workReceiptHome}`);
|
|
108
|
+
console.log(`本地执行器:${status.runtimeInstalled ? "已安装" : "未安装"}`);
|
|
109
|
+
console.log(`Codex Hook:${status.hookInstalled ? "已安装" : "未安装"}`);
|
|
110
|
+
if (status.state?.last_success_at) console.log(`最近自动生成:${status.state.last_success_at}`);
|
|
111
|
+
if (status.state?.import_file) console.log(`微信导入文件:${status.state.import_file}`);
|
|
112
|
+
if (status.state?.error) console.log(`最近状态:${status.state.error}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async function runSetup(options) {
|
|
117
|
+
if (!isInteractive()) throw new Error("--setup 需要在交互式终端中运行;也可以使用 --enable-auto 或 --disable-auto");
|
|
118
|
+
const selected = await promptForGenerationMode({ locale: options.locale });
|
|
119
|
+
return selected === "automatic" ? enableAuto(options) : enableManual(options);
|
|
120
|
+
}
|
|
121
|
+
|
|
50
122
|
async function main() {
|
|
51
123
|
const options = parseArgs(process.argv.slice(2));
|
|
52
124
|
if (options.help) {
|
|
53
125
|
printHelp(options.locale);
|
|
54
126
|
return;
|
|
55
127
|
}
|
|
128
|
+
if (options.setup) {
|
|
129
|
+
await runSetup(options);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (options.enableAuto) {
|
|
133
|
+
enableAuto(options);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (options.disableAuto) {
|
|
137
|
+
enableManual(options);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (options.autoStatus) {
|
|
141
|
+
printAutoStatus(options);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
56
144
|
if (options.installSkill) {
|
|
57
145
|
const installed = installCodexSkill({ projectDir: PROJECT_DIR });
|
|
58
146
|
if (options.locale === "en") {
|
|
@@ -95,7 +183,16 @@ async function main() {
|
|
|
95
183
|
return;
|
|
96
184
|
}
|
|
97
185
|
|
|
98
|
-
if (!options.modeExplicit &&
|
|
186
|
+
if (!options.modeExplicit && isInteractive()) {
|
|
187
|
+
const workReceiptHome = getWorkReceiptHome({ dataDir: options.dataDir });
|
|
188
|
+
if (!readAutoConfig({ workReceiptHome })) {
|
|
189
|
+
const selectedMode = await promptForGenerationMode({ locale: options.locale });
|
|
190
|
+
if (selectedMode === "automatic") {
|
|
191
|
+
enableAuto(options);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
enableManual(options);
|
|
195
|
+
}
|
|
99
196
|
const selected = await promptForRange({
|
|
100
197
|
locale: options.locale,
|
|
101
198
|
timezone: options.timezone,
|
|
@@ -106,47 +203,44 @@ async function main() {
|
|
|
106
203
|
options.hours = selected.hours || options.hours;
|
|
107
204
|
}
|
|
108
205
|
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
`codex-receipt-${outputSlugForRange(range, record.id)}.html`,
|
|
128
|
-
);
|
|
129
|
-
const outputFile = path.resolve(/\.html?$/i.test(requestedOutput) ? requestedOutput : `${requestedOutput}.html`);
|
|
130
|
-
fs.mkdirSync(path.dirname(outputFile), { recursive: true });
|
|
131
|
-
|
|
132
|
-
const miniProgramCodeDataUrl = imageAsDataUrl(DEFAULT_MINIPROGRAM_CODE);
|
|
133
|
-
|
|
134
|
-
fs.writeFileSync(
|
|
206
|
+
const generated = await generateReceipt(options, {
|
|
207
|
+
projectDir: PROJECT_DIR,
|
|
208
|
+
createDataQr: async (record) => {
|
|
209
|
+
const dataQr = encodeSingleReceiptQr(record);
|
|
210
|
+
if (!dataQr) return null;
|
|
211
|
+
return {
|
|
212
|
+
version: dataQr.version,
|
|
213
|
+
dataUrl: await QRCode.toDataURL(dataQr.payload, {
|
|
214
|
+
errorCorrectionLevel: "M",
|
|
215
|
+
margin: 2,
|
|
216
|
+
width: 360,
|
|
217
|
+
color: { dark: "#171713", light: "#ffffff" },
|
|
218
|
+
}),
|
|
219
|
+
};
|
|
220
|
+
},
|
|
221
|
+
});
|
|
222
|
+
const {
|
|
223
|
+
record,
|
|
135
224
|
outputFile,
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
225
|
+
persisted,
|
|
226
|
+
dataQrDataUrl,
|
|
227
|
+
dataQrVersion,
|
|
228
|
+
miniProgramCodeDataUrl,
|
|
229
|
+
} = generated;
|
|
140
230
|
|
|
141
231
|
if (options.locale === "en") {
|
|
142
232
|
console.log(`Generated HTML: ${outputFile}`);
|
|
143
233
|
console.log(`Structured data: ${persisted.companionPath}`);
|
|
234
|
+
console.log(`WeChat import file: ${persisted.transferPath}`);
|
|
144
235
|
console.log(`Local history: ${persisted.receiptPath}`);
|
|
145
236
|
console.log(`Range: ${getScopeLabel(record.source.scope, options.locale, record.source.hours)} · ${record.stats.session_count} session(s)`);
|
|
146
237
|
console.log(`Stats: ${record.stats.completed_turns} turns · ${formatNumber(record.stats.tokens.total_tokens, options.locale)} Tokens · ${record.stats.tool_calls} tool calls`);
|
|
238
|
+
console.log(dataQrDataUrl
|
|
239
|
+
? `Data QR: available as one code · QR version ${dataQrVersion}`
|
|
240
|
+
: "Data QR: not generated because the payload exceeds one code; use the WeChat import file");
|
|
147
241
|
console.log(record.manifest
|
|
148
|
-
? `
|
|
149
|
-
: `
|
|
242
|
+
? `Import data: ${record.manifest.fact_count} canonical fact(s) · schema v${record.schema_version}`
|
|
243
|
+
: `Import data: rolling summary · schema v${record.schema_version}`);
|
|
150
244
|
if (record.source.scope === "last-hours") {
|
|
151
245
|
console.log(`Note: ${getRollingSummaryNotice(options.locale, record.source.hours)}`);
|
|
152
246
|
}
|
|
@@ -154,12 +248,16 @@ async function main() {
|
|
|
154
248
|
} else {
|
|
155
249
|
console.log(`已生成网页:${outputFile}`);
|
|
156
250
|
console.log(`结构数据:${persisted.companionPath}`);
|
|
251
|
+
console.log(`微信导入文件:${persisted.transferPath}`);
|
|
157
252
|
console.log(`本地历史:${persisted.receiptPath}`);
|
|
158
253
|
console.log(`统计范围:${getScopeLabel(record.source.scope, options.locale, record.source.hours)} · ${record.stats.session_count} 个会话`);
|
|
159
254
|
console.log(`统计:${record.stats.completed_turns} 轮 · ${formatNumber(record.stats.tokens.total_tokens, options.locale)} Token · ${record.stats.tool_calls} 次工具调用`);
|
|
255
|
+
console.log(dataQrDataUrl
|
|
256
|
+
? `数据二维码:可用 · 单码 · QR version ${dataQrVersion}`
|
|
257
|
+
: "数据二维码:数据超过单码容量,已改用微信聊天文件导入");
|
|
160
258
|
console.log(record.manifest
|
|
161
|
-
?
|
|
162
|
-
:
|
|
259
|
+
? `导入数据:${record.manifest.fact_count} 条规范事实 · schema v${record.schema_version}`
|
|
260
|
+
: `导入数据:滚动摘要 · schema v${record.schema_version}`);
|
|
163
261
|
if (record.source.scope === "last-hours") {
|
|
164
262
|
console.log(`提示:${getRollingSummaryNotice(options.locale, record.source.hours)}`);
|
|
165
263
|
}
|
package/src/core/args.mjs
CHANGED
|
@@ -13,6 +13,7 @@ Usage:
|
|
|
13
13
|
npx codex-work-receipt@latest --range this-week --lang en
|
|
14
14
|
npx codex-work-receipt@latest --install-skill --lang en
|
|
15
15
|
npx codex-work-receipt@latest --install-companion --lang en
|
|
16
|
+
npx codex-work-receipt@latest --setup --lang en
|
|
16
17
|
|
|
17
18
|
Options:
|
|
18
19
|
--range <name> Range: latest, last-hours, today, last-7-days, this-week
|
|
@@ -29,6 +30,10 @@ Options:
|
|
|
29
30
|
--install-pet Install the Codex pet only
|
|
30
31
|
--uninstall-pet Remove the installed Codex pet
|
|
31
32
|
--install-companion Install both the skill and Codex pet
|
|
33
|
+
--setup Choose automatic saving or manual-only mode
|
|
34
|
+
--enable-auto Enable automatic daily receipt saving
|
|
35
|
+
--disable-auto Switch to manual-only mode
|
|
36
|
+
--auto-status Show automatic saving status
|
|
32
37
|
--no-open Do not open the browser after generation
|
|
33
38
|
--help Show help
|
|
34
39
|
` : `
|
|
@@ -43,6 +48,7 @@ Codex AI 打工小票
|
|
|
43
48
|
npx codex-work-receipt@latest --range this-week
|
|
44
49
|
npx codex-work-receipt@latest --install-skill
|
|
45
50
|
npx codex-work-receipt@latest --install-companion
|
|
51
|
+
npx codex-work-receipt@latest --setup
|
|
46
52
|
|
|
47
53
|
选项:
|
|
48
54
|
--range <name> 统计范围:latest、last-hours、today、last-7-days、this-week
|
|
@@ -59,6 +65,10 @@ Codex AI 打工小票
|
|
|
59
65
|
--install-pet 只安装 Codex 桌宠
|
|
60
66
|
--uninstall-pet 卸载 AI 打工小票 Codex 桌宠
|
|
61
67
|
--install-companion 同时安装 Skill 和 Codex 桌宠
|
|
68
|
+
--setup 选择自动保存或仅手动模式
|
|
69
|
+
--enable-auto 启用自动保存今日小票
|
|
70
|
+
--disable-auto 切换为仅手动模式
|
|
71
|
+
--auto-status 查看自动保存状态
|
|
62
72
|
--no-open 生成后不自动打开浏览器
|
|
63
73
|
--help 显示帮助
|
|
64
74
|
`);
|
|
@@ -79,6 +89,10 @@ export function parseArgs(argv) {
|
|
|
79
89
|
installPet: false,
|
|
80
90
|
uninstallPet: false,
|
|
81
91
|
installCompanion: false,
|
|
92
|
+
setup: false,
|
|
93
|
+
enableAuto: false,
|
|
94
|
+
disableAuto: false,
|
|
95
|
+
autoStatus: false,
|
|
82
96
|
open: true,
|
|
83
97
|
};
|
|
84
98
|
|
|
@@ -111,6 +125,10 @@ export function parseArgs(argv) {
|
|
|
111
125
|
result.mode = scope;
|
|
112
126
|
result.modeExplicit = true;
|
|
113
127
|
} else if (argument === "--install-skill") result.installSkill = true;
|
|
128
|
+
else if (argument === "--setup") result.setup = true;
|
|
129
|
+
else if (argument === "--enable-auto") result.enableAuto = true;
|
|
130
|
+
else if (argument === "--disable-auto") result.disableAuto = true;
|
|
131
|
+
else if (argument === "--auto-status") result.autoStatus = true;
|
|
114
132
|
else if (argument === "--no-open") result.open = false;
|
|
115
133
|
else if (argument === "--help" || argument === "-h") result.help = true;
|
|
116
134
|
else if (optionsWithValues.has(argument)) {
|
|
@@ -134,6 +152,11 @@ export function parseArgs(argv) {
|
|
|
134
152
|
if (!new Set(["zh-CN", "en"]).has(result.locale)) {
|
|
135
153
|
throw new Error(`不支持的语言:${result.locale}`);
|
|
136
154
|
}
|
|
155
|
+
try {
|
|
156
|
+
new Intl.DateTimeFormat("en-US", { timeZone: result.timezone }).format(new Date());
|
|
157
|
+
} catch {
|
|
158
|
+
throw new Error(`不支持的时区:${result.timezone}`);
|
|
159
|
+
}
|
|
137
160
|
if (result.hours !== null) {
|
|
138
161
|
result.hours = Number(result.hours);
|
|
139
162
|
if (!Number.isInteger(result.hours) || result.hours < 1 || result.hours > 168) {
|
|
@@ -141,5 +164,16 @@ export function parseArgs(argv) {
|
|
|
141
164
|
}
|
|
142
165
|
}
|
|
143
166
|
if (result.mode === "last-hours" && result.hours === null) result.hours = 3;
|
|
167
|
+
const managementActions = [
|
|
168
|
+
result.setup,
|
|
169
|
+
result.enableAuto,
|
|
170
|
+
result.disableAuto,
|
|
171
|
+
result.autoStatus,
|
|
172
|
+
].filter(Boolean).length;
|
|
173
|
+
if (managementActions > 1) throw new Error("自动保存管理参数不能同时使用");
|
|
174
|
+
if (managementActions && result.modeExplicit) throw new Error("自动保存管理参数不能与统计范围参数同时使用");
|
|
175
|
+
if (managementActions && (
|
|
176
|
+
result.installSkill || result.installPet || result.uninstallPet || result.installCompanion
|
|
177
|
+
)) throw new Error("自动保存管理参数不能与 Skill 或桌宠管理参数同时使用");
|
|
144
178
|
return result;
|
|
145
179
|
}
|