polycopy 0.1.7 → 0.1.9

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.
@@ -2,10 +2,10 @@
2
2
  const readline = require("readline");
3
3
  const fsExtra = require("fs-extra");
4
4
  const path = require("path");
5
- const paths = require("./paths-CEjGES8j.js");
6
5
  const grammy = require("grammy");
7
- const winston = require("winston");
8
- const DailyRotateFile = require("winston-daily-rotate-file");
6
+ const child_process = require("child_process");
7
+ const fs = require("fs");
8
+ const os = require("os");
9
9
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
10
10
  function _interopNamespace(e) {
11
11
  if (e && e.__esModule) return e;
@@ -27,13 +27,21 @@ function _interopNamespace(e) {
27
27
  const readline__namespace = /* @__PURE__ */ _interopNamespace(readline);
28
28
  const fsExtra__default = /* @__PURE__ */ _interopDefault(fsExtra);
29
29
  const path__default = /* @__PURE__ */ _interopDefault(path);
30
- const winston__default = /* @__PURE__ */ _interopDefault(winston);
31
- const DailyRotateFile__default = /* @__PURE__ */ _interopDefault(DailyRotateFile);
30
+ const fs__namespace = /* @__PURE__ */ _interopNamespace(fs);
31
+ const os__default = /* @__PURE__ */ _interopDefault(os);
32
+ const USER_DATA_DIR = path__default.default.join(os__default.default.homedir(), ".polycopy");
33
+ const PID_DIR = USER_DATA_DIR;
34
+ const LOGS_DIR = path__default.default.join(USER_DATA_DIR, "logs");
35
+ const LOCALSTORAGE_DIR = path__default.default.join(USER_DATA_DIR, "localstorage");
36
+ const PID_FILE = path__default.default.join(USER_DATA_DIR, "polycopy.pid");
37
+ const INSTALL_ROOT = path__default.default.resolve(__dirname, "..");
38
+ const DIST_INDEX = path__default.default.join(INSTALL_ROOT, "dist", "index.js");
39
+ const DIST_CLI = path__default.default.join(INSTALL_ROOT, "dist", "cli.js");
32
40
  class LocalStorage {
33
41
  dbPath;
34
42
  data;
35
43
  constructor(dbFileName) {
36
- this.dbPath = path__default.default.join(paths.LOCALSTORAGE_DIR, `${dbFileName}.json`);
44
+ this.dbPath = path__default.default.join(LOCALSTORAGE_DIR, `${dbFileName}.json`);
37
45
  if (fsExtra__default.default.existsSync(this.dbPath)) {
38
46
  this.data = fsExtra__default.default.readJSONSync(this.dbPath);
39
47
  } else {
@@ -65,153 +73,12 @@ function hasPolyMarketConfig(config) {
65
73
  }
66
74
  function hasTradingConfig(config) {
67
75
  const trading = config.trading;
68
- return !!(trading?.amountMode && trading?.amountValue !== void 0 && trading?.amountValue !== null && trading?.buyPrice !== void 0 && trading?.buyPrice !== null && trading?.sellPrice !== void 0 && trading?.sellPrice !== null);
76
+ const amountValue = trading?.amountMode === "fixed" ? trading.amountFixedValue : trading?.amountMode === "percentage" ? trading.amountPercentageValue : void 0;
77
+ return !!(trading?.amountMode && amountValue !== void 0 && amountValue !== null && trading?.buyPrice !== void 0 && trading?.buyPrice !== null && trading?.sellPrice !== void 0 && trading?.sellPrice !== null);
69
78
  }
70
79
  function canAutoTrade(config) {
71
80
  return hasPolyMarketConfig(config) && hasTradingConfig(config);
72
81
  }
73
- const formatTimestamp = () => {
74
- const now = /* @__PURE__ */ new Date();
75
- const pad = (n) => n.toString().padStart(2, "0");
76
- return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`;
77
- };
78
- const customFormat = winston__default.default.format.printf(({ message }) => {
79
- return `[${formatTimestamp()}] ${message}`;
80
- });
81
- const fileTransport = new DailyRotateFile__default.default({
82
- dirname: paths.LOGS_DIR,
83
- filename: "%DATE%",
84
- extension: ".log",
85
- datePattern: "YYYY-MM-DD",
86
- // 按天创建文件
87
- maxSize: "2m",
88
- // 超过 2MB 自动轮转(会在文件名后加序号)
89
- maxFiles: 10,
90
- // 最多 10 个文件
91
- format: customFormat,
92
- // 不创建 current.log 符号链接
93
- createSymlink: false
94
- });
95
- const winstonLogger = winston__default.default.createLogger({
96
- level: "info",
97
- transports: [fileTransport]
98
- });
99
- class Logger {
100
- initialized = false;
101
- flushing = false;
102
- /**
103
- * 初始化日志模块(输出日志路径信息)
104
- */
105
- init() {
106
- if (this.initialized) return;
107
- this.initialized = true;
108
- console.log(`
109
- 📁 日志目录: ${paths.LOGS_DIR}`);
110
- }
111
- /**
112
- * 获取带时间前缀的消息(用于控制台输出)
113
- */
114
- withTimestamp(message) {
115
- return `[${formatTimestamp()}] ${message}`;
116
- }
117
- /**
118
- * 写入日志(同时输出到控制台和文件)
119
- */
120
- write(message, addNewLine = true) {
121
- const consoleMsg = addNewLine ? `
122
- ${this.withTimestamp(message)}` : this.withTimestamp(message);
123
- console.log(consoleMsg);
124
- winstonLogger.info(message);
125
- }
126
- /**
127
- * 输出信息日志(前后自动添加空行)
128
- */
129
- info(message, ...args) {
130
- const formatted = args.length > 0 ? `${message} ${args.join(" ")}` : message;
131
- this.write(formatted);
132
- }
133
- /**
134
- * 输出成功日志(前后自动添加空行)
135
- */
136
- success(message, ...args) {
137
- const formatted = args.length > 0 ? `${message} ${args.join(" ")}` : message;
138
- this.write(`✅ ${formatted}`);
139
- }
140
- /**
141
- * 输出警告日志(前后自动添加空行)
142
- */
143
- warning(message, ...args) {
144
- const formatted = args.length > 0 ? `${message} ${args.join(" ")}` : message;
145
- this.write(`⚠️ ${formatted}`);
146
- }
147
- /**
148
- * 输出错误日志(前后自动添加空行)
149
- */
150
- error(message, ...args) {
151
- const formatted = args.length > 0 ? `${message} ${args.join(" ")}` : message;
152
- console.error(`
153
- ${this.withTimestamp(`❌ ${formatted}`)}`);
154
- winstonLogger.error(`❌ ${formatted}`);
155
- }
156
- /**
157
- * 输出章节标题(前后自动添加空行)
158
- */
159
- section(title) {
160
- this.write(`=== ${title} ===`);
161
- }
162
- /**
163
- * 输出列表项(不添加空行,用于连续输出)
164
- */
165
- item(message, indent = 0) {
166
- const indentStr = " ".repeat(indent);
167
- const formatted = `${indentStr}${message}`;
168
- console.log(this.withTimestamp(formatted));
169
- winstonLogger.info(formatted);
170
- }
171
- /**
172
- * 输出普通文本(不添加空行,用于连续输出)
173
- */
174
- text(message, ...args) {
175
- const formatted = args.length > 0 ? `${message} ${args.join(" ")}` : message;
176
- console.log(this.withTimestamp(formatted));
177
- winstonLogger.info(formatted);
178
- }
179
- /**
180
- * 输出空行
181
- */
182
- blank() {
183
- console.log("");
184
- }
185
- /**
186
- * 输出带前缀的信息(不添加空行)
187
- * 用于在同一组日志中输出多行信息
188
- */
189
- line(prefix, message, ...args) {
190
- const formatted = args.length > 0 ? ` ${prefix} ${message} ${args.join(" ")}` : ` ${prefix} ${message}`;
191
- console.log(this.withTimestamp(formatted));
192
- winstonLogger.info(formatted);
193
- }
194
- /**
195
- * 刷新日志输出(用于进程退出前)
196
- */
197
- async flush(timeoutMs = 1e3) {
198
- if (this.flushing) {
199
- return;
200
- }
201
- this.flushing = true;
202
- await new Promise((resolve) => {
203
- const timeout = setTimeout(() => {
204
- resolve();
205
- }, timeoutMs);
206
- winstonLogger.once("finish", () => {
207
- clearTimeout(timeout);
208
- resolve();
209
- });
210
- winstonLogger.end();
211
- });
212
- }
213
- }
214
- const logger = new Logger();
215
82
  let globalRl = null;
216
83
  function getReadlineInterface() {
217
84
  if (!globalRl) {
@@ -222,6 +89,12 @@ function getReadlineInterface() {
222
89
  }
223
90
  return globalRl;
224
91
  }
92
+ function closeReadlineInterface() {
93
+ if (globalRl) {
94
+ globalRl.close();
95
+ globalRl = null;
96
+ }
97
+ }
225
98
  function question(query) {
226
99
  const rl = getReadlineInterface();
227
100
  return new Promise((resolve) => {
@@ -234,12 +107,12 @@ async function validateBotToken(botToken) {
234
107
  await bot.api.getMe();
235
108
  return true;
236
109
  } catch (error) {
237
- logger.error(`Bot Token 验证失败: ${error}`);
110
+ console.log(`❌ Bot Token 验证失败: ${error}`);
238
111
  return false;
239
112
  }
240
113
  }
241
114
  async function runConfigWizard(existingConfig) {
242
- logger.section("PolyMarket 跟单机器人配置向导");
115
+ console.log("\n=== PolyMarket 跟单机器人配置向导 ===");
243
116
  const currentTelegram = existingConfig?.telegram || configLocal.getItem("telegram");
244
117
  const currentPolymarket = existingConfig?.polymarket || configLocal.getItem("polymarket");
245
118
  const currentTrading = existingConfig?.trading || configLocal.getItem("trading");
@@ -247,7 +120,7 @@ async function runConfigWizard(existingConfig) {
247
120
  while (!botToken) {
248
121
  const currentToken = currentTelegram?.botToken || "";
249
122
  const currentDisplay = currentToken ? `${currentToken.substring(0, 10)}...` : "未配置";
250
- const hint = currentToken ? "回车保持,或输入新值" : "必填";
123
+ const hint = currentToken ? "回车保持,或输入新值" : "必填,前往 https://t.me/BotFather 申请";
251
124
  const input = await question(
252
125
  ` - Bot Token [${currentDisplay}] (${hint}): `
253
126
  );
@@ -258,6 +131,7 @@ async function runConfigWizard(existingConfig) {
258
131
  }
259
132
  if (!botToken) {
260
133
  console.log("❌ Bot Token 不能为空");
134
+ console.log(" 没有 Bot 请前往 https://t.me/BotFather 申请");
261
135
  continue;
262
136
  }
263
137
  console.log("正在验证...");
@@ -285,8 +159,8 @@ async function runConfigWizard(existingConfig) {
285
159
  console.log("Chat ID 格式无效,已跳过");
286
160
  }
287
161
  }
288
- logger.section("PolyMarket 配置(可选)");
289
- logger.text("提示: 以下配置项都可以跳过,留空后将无法自动跟单");
162
+ console.log("\n=== PolyMarket 配置(可选) ===");
163
+ console.log("提示: 以下配置项都可以跳过,留空后将无法自动跟单");
290
164
  const currentPrivateKey = currentPolymarket?.privateKey || "";
291
165
  const pkDisplay = currentPrivateKey ? "已配置" : "未配置";
292
166
  const pkHint = currentPrivateKey ? "回车保持,skip 清除" : "回车跳过";
@@ -309,7 +183,7 @@ async function runConfigWizard(existingConfig) {
309
183
  }
310
184
  const hasBuilderCreds = !!builderCreds;
311
185
  const builderDisplay = hasBuilderCreds ? "已配置" : "未配置";
312
- const builderHint = hasBuilderCreds ? "y=重新配置, n=清除, 回车=保持" : "y=配置, 回车=跳过, 前往 polymarket.com/settings?tab=builder 获取";
186
+ const builderHint = hasBuilderCreds ? "y=重新配置, n=清除, 回车=保持" : "y=配置, 回车=跳过, 前往 https://polymarket.com/settings?tab=builder 申请";
313
187
  const needBuilderCreds = await question(
314
188
  ` - Builder API [${builderDisplay}] (${builderHint}): `
315
189
  );
@@ -345,7 +219,7 @@ async function runConfigWizard(existingConfig) {
345
219
  builderCreds = void 0;
346
220
  }
347
221
  let tradingConfig = currentTrading || {};
348
- logger.section("交易配置(可选)");
222
+ console.log("\n=== 交易配置(可选) ===");
349
223
  const tradingStatus = currentTrading && Object.keys(currentTrading).length > 0 ? "已配置" : "未配置";
350
224
  const needTradingConfig = await question(
351
225
  ` - 交易配置 [${tradingStatus}] (y=配置, 回车=跳过): `
@@ -369,44 +243,45 @@ async function runConfigWizard(existingConfig) {
369
243
  trading: tradingConfig
370
244
  };
371
245
  if (tradingConfig.sellPrice === 1 && !builderCreds) {
372
- logger.warning(
373
- "sellPrice = 1 但未配置 builderCreds,Redeem 功能将无法使用"
374
- );
375
- logger.line(
376
- "",
377
- "请配置 Builder API 凭证,或将卖价设置为 < 1 使用限价卖出模式"
246
+ console.log(
247
+ "⚠️ sellPrice = 1 但未配置 builderCreds,Redeem 功能将无法使用"
378
248
  );
249
+ console.log(" 请配置 Builder API 凭证,或将卖价设置为 < 1 使用限价卖出模式");
379
250
  }
380
251
  configLocal.setItem("telegram", config.telegram);
381
252
  configLocal.setItem("polymarket", config.polymarket);
382
253
  const cleanTradingConfig = {};
383
254
  if (config.trading.amountMode !== void 0)
384
255
  cleanTradingConfig.amountMode = config.trading.amountMode;
385
- if (config.trading.amountValue !== void 0)
386
- cleanTradingConfig.amountValue = config.trading.amountValue;
256
+ if (config.trading.amountFixedValue !== void 0)
257
+ cleanTradingConfig.amountFixedValue = config.trading.amountFixedValue;
258
+ if (config.trading.amountPercentageValue !== void 0)
259
+ cleanTradingConfig.amountPercentageValue = config.trading.amountPercentageValue;
387
260
  if (config.trading.buyPrice !== void 0)
388
261
  cleanTradingConfig.buyPrice = config.trading.buyPrice;
389
262
  if (config.trading.sellPrice !== void 0)
390
263
  cleanTradingConfig.sellPrice = config.trading.sellPrice;
391
264
  configLocal.setItem("trading", cleanTradingConfig);
392
- logger.success("配置已保存!");
265
+ console.log("配置已保存!");
393
266
  if (privateKeyTrimmed) {
394
- logger.success("PolyMarket 配置已保存!");
267
+ console.log("PolyMarket 配置已保存!");
395
268
  if (Object.keys(tradingConfig).length > 0) {
396
- logger.success("交易配置已保存!");
269
+ console.log("交易配置已保存!");
397
270
  } else {
398
- logger.info("ℹ️ 已跳过交易配置");
271
+ console.log("ℹ️ 已跳过交易配置");
399
272
  }
400
273
  } else {
401
- logger.info("ℹ️ 已跳过 PolyMarket 配置");
274
+ console.log("ℹ️ 已跳过 PolyMarket 配置");
402
275
  }
403
276
  return config;
404
277
  }
405
278
  async function initConfig() {
406
- return runConfigWizard();
279
+ const config = await runConfigWizard();
280
+ closeReadlineInterface();
281
+ return config;
407
282
  }
408
283
  async function configureTrading(existingConfig) {
409
- logger.section("交易配置");
284
+ console.log("\n=== 交易配置 ===");
410
285
  console.log("提示: 配置不完整时将无法自动跟单");
411
286
  const existingMode = existingConfig?.amountMode;
412
287
  const defaultMode = existingMode || "fixed";
@@ -431,34 +306,35 @@ async function configureTrading(existingConfig) {
431
306
  console.log("无效选择,请输入 1 或 2\n");
432
307
  }
433
308
  }
434
- const existingValue = existingConfig?.amountValue;
309
+ const existingFixedValue = existingConfig?.amountFixedValue;
310
+ const existingPercentageValue = existingConfig?.amountPercentageValue;
311
+ const defaultFixedValue = 5;
312
+ const defaultPercentageValue = 0.1;
435
313
  let amountValue;
436
314
  while (true) {
437
- const unit = amountMode === "fixed" ? " USDC" : "";
438
- const valueDisplay = existingValue !== void 0 ? `${existingValue}${unit}` : "未配置";
439
- const valueHint = amountMode === "fixed" ? "USDC金额" : "0-1比例";
315
+ const isFixed = amountMode === "fixed";
316
+ const unit = isFixed ? " USDC" : "";
317
+ const existingValue = isFixed ? existingFixedValue : existingPercentageValue;
318
+ const defaultValue = isFixed ? defaultFixedValue : defaultPercentageValue;
319
+ const valueDisplay = existingValue !== void 0 ? `${existingValue}${unit}` : `${defaultValue}${unit}`;
320
+ const valueHint = isFixed ? "USDC金额" : "0-1比例";
440
321
  const valueInput = await question(
441
- ` - 使用金数值 [${valueDisplay}] (${valueHint}, skip=跳过): `
322
+ ` - 使用金数值 [${valueDisplay}] (${valueHint}): `
442
323
  );
443
324
  if (!valueInput.trim()) {
444
325
  if (existingValue !== void 0) {
445
326
  amountValue = existingValue;
446
- break;
447
327
  } else {
448
- amountValue = void 0;
449
- break;
328
+ amountValue = defaultValue;
450
329
  }
451
- }
452
- if (valueInput.trim().toLowerCase() === "skip") {
453
- amountValue = void 0;
454
330
  break;
455
331
  }
456
332
  const value = parseFloat(valueInput.trim());
457
333
  if (isNaN(value) || value <= 0) {
458
- console.log("请输入有效的正数,或输入 'skip' 跳过\n");
334
+ console.log("请输入有效的正数\n");
459
335
  continue;
460
336
  }
461
- if (amountMode === "percentage" && value > 1) {
337
+ if (!isFixed && value > 1) {
462
338
  console.log("比例不能大于 1,请重新输入\n");
463
339
  continue;
464
340
  }
@@ -471,19 +347,15 @@ async function configureTrading(existingConfig) {
471
347
  while (true) {
472
348
  const buyDisplay = existingBuyPrice !== void 0 ? existingBuyPrice : defaultBuyPrice;
473
349
  const buyPriceInput = await question(
474
- ` - 买价 [${buyDisplay}] (0.1-0.99, skip=跳过): `
350
+ ` - 买价 [${buyDisplay}] (0.1-0.99): `
475
351
  );
476
352
  if (!buyPriceInput.trim()) {
477
353
  buyPrice = existingBuyPrice ?? defaultBuyPrice;
478
354
  break;
479
355
  }
480
- if (buyPriceInput.trim().toLowerCase() === "skip") {
481
- buyPrice = void 0;
482
- break;
483
- }
484
356
  const price = parseFloat(buyPriceInput.trim());
485
357
  if (isNaN(price) || price < 0.1 || price > 0.99) {
486
- console.log("买价必须在 0.1 到 0.99 之间,或输入 'skip' 跳过\n");
358
+ console.log("买价必须在 0.1 到 0.99 之间\n");
487
359
  continue;
488
360
  }
489
361
  buyPrice = price;
@@ -496,22 +368,16 @@ async function configureTrading(existingConfig) {
496
368
  const sellDisplay = String(existingSellPrice ?? defaultSellPrice);
497
369
  const minSell = buyPrice ?? 0.98;
498
370
  const sellPriceInput = await question(
499
- ` - 卖价 [${sellDisplay}] (>${minSell} 且 <=1, 1=不卖出等待自动 redeem, skip=跳过): `
371
+ ` - 卖价 [${sellDisplay}] (>${minSell} 且 <=1, 1=不卖出等待自动 redeem): `
500
372
  );
501
373
  if (!sellPriceInput.trim()) {
502
374
  sellPrice = existingSellPrice ?? defaultSellPrice;
503
375
  break;
504
376
  }
505
- if (sellPriceInput.trim().toLowerCase() === "skip") {
506
- sellPrice = void 0;
507
- break;
508
- }
509
377
  const price = parseFloat(sellPriceInput.trim());
510
378
  if (isNaN(price) || buyPrice !== void 0 && price <= buyPrice || price > 1) {
511
- console.log(
512
- `卖价必须大于 ${buyPrice ?? 0.98} 且小于等于 1,或输入 'skip' 跳过
513
- `
514
- );
379
+ console.log(`卖价必须大于 ${buyPrice ?? 0.98} 且小于等于 1
380
+ `);
515
381
  continue;
516
382
  }
517
383
  sellPrice = price;
@@ -519,22 +385,22 @@ async function configureTrading(existingConfig) {
519
385
  }
520
386
  const tradingConfig = {
521
387
  amountMode,
522
- amountValue,
388
+ amountFixedValue: amountMode === "fixed" ? amountValue : existingFixedValue,
389
+ amountPercentageValue: amountMode === "percentage" ? amountValue : existingPercentageValue,
523
390
  buyPrice,
524
391
  sellPrice
525
392
  };
526
- logger.success("交易配置已保存!");
393
+ console.log("交易配置已保存!");
527
394
  if (tradingConfig.sellPrice === 1) {
528
- logger.line("ℹ️", "卖价设置为 1,将使用自动 redeem 模式");
395
+ console.log("ℹ️ 卖价设置为 1,将使用自动 redeem 模式");
529
396
  } else if (tradingConfig.sellPrice !== void 0) {
530
- logger.line(
531
- "ℹ️",
532
- `卖价设置为 ${tradingConfig.sellPrice},将使用限价卖出模式`
397
+ console.log(
398
+ `ℹ️ 卖价设置为 ${tradingConfig.sellPrice},将使用限价卖出模式`
533
399
  );
534
400
  }
535
- const hasAllConfig = tradingConfig.amountMode && tradingConfig.amountValue !== void 0 && tradingConfig.buyPrice !== void 0 && tradingConfig.sellPrice !== void 0;
401
+ const hasAllConfig = tradingConfig.amountMode && (tradingConfig.amountMode === "fixed" ? tradingConfig.amountFixedValue !== void 0 : tradingConfig.amountPercentageValue !== void 0) && tradingConfig.buyPrice !== void 0 && tradingConfig.sellPrice !== void 0;
536
402
  if (!hasAllConfig) {
537
- logger.warning("交易配置不完整,将无法自动跟单");
403
+ console.log("⚠️ 交易配置不完整,将无法自动跟单");
538
404
  }
539
405
  return tradingConfig;
540
406
  }
@@ -543,8 +409,10 @@ async function ensureConfig() {
543
409
  const polymarket = configLocal.getItem("polymarket");
544
410
  const trading = configLocal.getItem("trading");
545
411
  if (!telegram && !polymarket && !trading) {
546
- logger.info("未找到配置文件,开始初始化配置...");
547
- return initConfig();
412
+ console.log("未找到配置文件,开始初始化配置...");
413
+ const updatedConfig = await initConfig();
414
+ closeReadlineInterface();
415
+ return updatedConfig;
548
416
  }
549
417
  const config = {
550
418
  telegram: telegram || { botToken: "" },
@@ -553,20 +421,24 @@ async function ensureConfig() {
553
421
  };
554
422
  const validation = validateConfig(config);
555
423
  if (!validation.valid) {
556
- logger.warning("配置文件不完整,缺少以下字段:");
557
- validation.missingFields.forEach((field) => logger.item(`- ${field}`, 1));
558
- logger.info("开始重新配置...");
559
- return initConfig();
424
+ console.log("⚠️ 配置文件不完整,缺少以下字段:");
425
+ validation.missingFields.forEach((field) => console.log(`- ${field}`));
426
+ console.log("开始重新配置...");
427
+ const updatedConfig = await initConfig();
428
+ closeReadlineInterface();
429
+ return updatedConfig;
560
430
  }
561
431
  if (!hasPolyMarketConfig(config)) {
562
- logger.info("未检测到 PolyMarket 配置");
432
+ console.log("未检测到 PolyMarket 配置");
563
433
  const useFullWizard = await question(
564
434
  "是否要运行完整配置向导?(y/n,默认 n,选择 n 将跳过配置): "
565
435
  );
566
436
  if (useFullWizard.trim().toLowerCase() === "y") {
567
- return runConfigWizard(config);
437
+ const updatedConfig = await runConfigWizard(config);
438
+ closeReadlineInterface();
439
+ return updatedConfig;
568
440
  } else {
569
- logger.info("ℹ️ 已跳过 PolyMarket 配置");
441
+ console.log("ℹ️ 已跳过 PolyMarket 配置");
570
442
  }
571
443
  }
572
444
  if (hasPolyMarketConfig(config) && !hasTradingConfig(config)) {
@@ -582,8 +454,10 @@ async function ensureConfig() {
582
454
  const cleanTradingConfig = {};
583
455
  if (config.trading.amountMode !== void 0)
584
456
  cleanTradingConfig.amountMode = config.trading.amountMode;
585
- if (config.trading.amountValue !== void 0)
586
- cleanTradingConfig.amountValue = config.trading.amountValue;
457
+ if (config.trading.amountFixedValue !== void 0)
458
+ cleanTradingConfig.amountFixedValue = config.trading.amountFixedValue;
459
+ if (config.trading.amountPercentageValue !== void 0)
460
+ cleanTradingConfig.amountPercentageValue = config.trading.amountPercentageValue;
587
461
  if (config.trading.buyPrice !== void 0)
588
462
  cleanTradingConfig.buyPrice = config.trading.buyPrice;
589
463
  if (config.trading.sellPrice !== void 0)
@@ -593,8 +467,98 @@ async function ensureConfig() {
593
467
  }
594
468
  return config;
595
469
  }
470
+ const normalizePath = (value) => value.replace(/\\/g, "/").toLowerCase();
471
+ const distIndexPath = normalizePath(DIST_INDEX);
472
+ const distCliPath = normalizePath(DIST_CLI);
473
+ const normalizeCommand = (command) => command.replace(/\\/g, "/");
474
+ const isPolycopyCommand = (command) => {
475
+ const normalized = normalizeCommand(command).toLowerCase();
476
+ return normalized.includes(distIndexPath) || normalized.includes(distCliPath) || normalized.includes("dist/cli.js");
477
+ };
478
+ const findPolycopyPids = () => {
479
+ if (process.platform === "win32") {
480
+ return [];
481
+ }
482
+ const result = child_process.spawnSync("ps", ["-ax", "-o", "pid=,command="], {
483
+ encoding: "utf-8"
484
+ });
485
+ if (result.status !== 0) {
486
+ return [];
487
+ }
488
+ return result.stdout.split("\n").map((line) => line.trim()).filter(Boolean).map((line) => {
489
+ const firstSpace = line.indexOf(" ");
490
+ if (firstSpace === -1) return null;
491
+ const pidText = line.slice(0, firstSpace).trim();
492
+ const command = line.slice(firstSpace + 1).trim();
493
+ const pid = parseInt(pidText, 10);
494
+ if (Number.isNaN(pid)) return null;
495
+ if (pid === process.pid) return null;
496
+ if (!isPolycopyCommand(command)) return null;
497
+ return pid;
498
+ }).filter((pid) => pid !== null);
499
+ };
500
+ const resolveRunningPid = () => {
501
+ const pid = getRunningPid();
502
+ if (pid) {
503
+ return pid;
504
+ }
505
+ const fallbackPids = findPolycopyPids();
506
+ return fallbackPids.length > 0 ? fallbackPids[0] : null;
507
+ };
508
+ const ensurePidDir = () => {
509
+ if (!fs__namespace.existsSync(PID_DIR)) {
510
+ fs__namespace.mkdirSync(PID_DIR, { recursive: true });
511
+ }
512
+ };
513
+ const getRunningPid = () => {
514
+ if (!fs__namespace.existsSync(PID_FILE)) {
515
+ return null;
516
+ }
517
+ try {
518
+ const pid = parseInt(fs__namespace.readFileSync(PID_FILE, "utf-8").trim());
519
+ process.kill(pid, 0);
520
+ if (!isPolycopyProcess(pid)) {
521
+ fs__namespace.unlinkSync(PID_FILE);
522
+ return null;
523
+ }
524
+ return pid;
525
+ } catch {
526
+ fs__namespace.unlinkSync(PID_FILE);
527
+ return null;
528
+ }
529
+ };
530
+ const savePid = (pid) => {
531
+ ensurePidDir();
532
+ fs__namespace.writeFileSync(PID_FILE, pid.toString());
533
+ };
534
+ const removePid = () => {
535
+ if (fs__namespace.existsSync(PID_FILE)) {
536
+ fs__namespace.unlinkSync(PID_FILE);
537
+ }
538
+ };
539
+ const isPolycopyProcess = (pid) => {
540
+ if (process.platform === "win32") {
541
+ return true;
542
+ }
543
+ const result = child_process.spawnSync("ps", ["-p", String(pid), "-o", "command="], {
544
+ encoding: "utf-8"
545
+ });
546
+ if (result.status !== 0) {
547
+ return false;
548
+ }
549
+ const command = result.stdout.trim();
550
+ if (!command) {
551
+ return false;
552
+ }
553
+ return isPolycopyCommand(command);
554
+ };
555
+ exports.LOGS_DIR = LOGS_DIR;
556
+ exports.PID_DIR = PID_DIR;
557
+ exports.PID_FILE = PID_FILE;
596
558
  exports.canAutoTrade = canAutoTrade;
597
559
  exports.configLocal = configLocal;
598
560
  exports.ensureConfig = ensureConfig;
599
- exports.logger = logger;
600
- exports.runConfigWizard = runConfigWizard;
561
+ exports.initConfig = initConfig;
562
+ exports.removePid = removePid;
563
+ exports.resolveRunningPid = resolveRunningPid;
564
+ exports.savePid = savePid;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polycopy",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "polycopy test",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -10,12 +10,12 @@
10
10
  "typecheck": "tsc --noEmit",
11
11
  "build": "tsc --noEmit && vite build",
12
12
  "start": "node dist/index.js",
13
- "config": "node dist/config.js",
14
13
  "dev": "tsc --noEmit && vite build && node dist/index.js",
15
14
  "cli": "node dist/cli.js",
16
15
  "prepublishOnly": "npm run build",
17
16
  "test": "HOME=$PWD node --import tsx tests/redeemer.test.ts",
18
- "test:redeemer": "HOME=$PWD node --import tsx tests/redeemer.test.ts"
17
+ "test:redeemer": "HOME=$PWD node --import tsx tests/redeemer.test.ts",
18
+ "test:log-follow": "node scripts/test-log-follow.cjs"
19
19
  },
20
20
  "dependencies": {
21
21
  "@polymarket/builder-relayer-client": "^0.0.8",
@@ -25,8 +25,7 @@
25
25
  "lodash": "^4.17.21",
26
26
  "set-promise-interval": "^1.1.0",
27
27
  "viem": "^2.44.2",
28
- "winston": "^3.19.0",
29
- "winston-daily-rotate-file": "^5.0.0"
28
+ "log4js": "^6.9.1"
30
29
  },
31
30
  "devDependencies": {
32
31
  "@types/fs-extra": "^11.0.4",
package/dist/config.js DELETED
@@ -1,13 +0,0 @@
1
- "use strict";
2
- const init = require("./init-Bs-N_AZi.js");
3
- async function main() {
4
- try {
5
- await init.runConfigWizard();
6
- init.logger.success("配置完成!");
7
- process.exit(0);
8
- } catch (error) {
9
- init.logger.error("配置过程中出错:", error);
10
- process.exit(1);
11
- }
12
- }
13
- main();
@@ -1,20 +0,0 @@
1
- "use strict";
2
- const os = require("os");
3
- const path = require("path");
4
- const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
5
- const os__default = /* @__PURE__ */ _interopDefault(os);
6
- const path__default = /* @__PURE__ */ _interopDefault(path);
7
- const USER_DATA_DIR = path__default.default.join(os__default.default.homedir(), ".polycopy");
8
- const PID_DIR = USER_DATA_DIR;
9
- const LOGS_DIR = path__default.default.join(USER_DATA_DIR, "logs");
10
- const LOCALSTORAGE_DIR = path__default.default.join(USER_DATA_DIR, "localstorage");
11
- const PID_FILE = path__default.default.join(USER_DATA_DIR, "polycopy.pid");
12
- const INSTALL_ROOT = path__default.default.resolve(__dirname, "..");
13
- const DIST_INDEX = path__default.default.join(INSTALL_ROOT, "dist", "index.js");
14
- const DIST_CLI = path__default.default.join(INSTALL_ROOT, "dist", "cli.js");
15
- exports.DIST_CLI = DIST_CLI;
16
- exports.DIST_INDEX = DIST_INDEX;
17
- exports.LOCALSTORAGE_DIR = LOCALSTORAGE_DIR;
18
- exports.LOGS_DIR = LOGS_DIR;
19
- exports.PID_DIR = PID_DIR;
20
- exports.PID_FILE = PID_FILE;