ones-fetch 1.0.0 → 1.1.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 CHANGED
@@ -11,10 +11,15 @@ npx ones-fetch
11
11
  ```
12
12
 
13
13
  这会自动:
14
- - 安装依赖(约 30MB
14
+ - 安装依赖(约 30MB,仅首次需要)
15
15
  - 在桌面创建快捷方式
16
16
  - 完成后双击桌面图标即可使用
17
17
 
18
+ **注意**:
19
+ - 依赖只需安装一次,后续启动无需重新安装
20
+ - 服务器会在浏览器关闭 5 分钟后自动退出
21
+ - 快捷方式使用 Windows 脚本宿主的默认图标
22
+
18
23
  ---
19
24
 
20
25
  ## 环境要求
package/bin/install.mjs CHANGED
@@ -14,7 +14,7 @@ const projectRoot = join(__dirname, '..');
14
14
  async function createWindowsShortcut() {
15
15
  const desktop = join(homedir(), 'Desktop');
16
16
  const shortcutPath = join(desktop, 'ONES 采集工具.lnk');
17
- const iconPath = join(projectRoot, 'public', 'icon.png');
17
+ const iconPath = join(projectRoot, 'public', 'icon.ico');
18
18
  const vbsLauncher = join(projectRoot, 'public', 'launcher.vbs');
19
19
 
20
20
  // 创建 PowerShell 脚本来生成快捷方式
@@ -24,7 +24,7 @@ $Shortcut = $WshShell.CreateShortcut("${shortcutPath.replace(/\\/g, '\\\\')}")
24
24
  $Shortcut.TargetPath = "wscript.exe"
25
25
  $Shortcut.Arguments = '"${vbsLauncher.replace(/\\/g, '\\\\')}"'
26
26
  $Shortcut.WorkingDirectory = "${projectRoot.replace(/\\/g, '\\\\')}"
27
- $Shortcut.IconLocation = "${iconPath.replace(/\\/g, '\\\\')}"
27
+ $Shortcut.IconLocation = "${iconPath.replace(/\\/g, '\\\\')},0"
28
28
  $Shortcut.Description = "ONES 任务采集工具"
29
29
  $Shortcut.Save()
30
30
  `;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ones-fetch",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "type": "module",
5
5
  "description": "ONES Fetch — Web app for recursive ONES subtask crawling",
6
6
  "bin": {
@@ -13,7 +13,12 @@
13
13
  "dependencies": {
14
14
  "playwright-core": "^1.58.2"
15
15
  },
16
- "keywords": ["ones", "task", "crawler", "project-management"],
16
+ "keywords": [
17
+ "ones",
18
+ "task",
19
+ "crawler",
20
+ "project-management"
21
+ ],
17
22
  "author": "",
18
23
  "license": "MIT"
19
24
  }
Binary file
package/public/index.html CHANGED
@@ -99,6 +99,23 @@ let allTasks = [];
99
99
  let collapsed = new Set();
100
100
  let authPollingTimer = null;
101
101
  let authContext = { baseUrl: '', teamId: '' };
102
+ let heartbeatTimer = null;
103
+
104
+ // Start heartbeat to keep server alive
105
+ function startHeartbeat() {
106
+ if (heartbeatTimer) clearInterval(heartbeatTimer);
107
+ heartbeatTimer = setInterval(() => {
108
+ fetch('/api/heartbeat').catch(() => {});
109
+ }, 60000); // Every 60 seconds
110
+ }
111
+
112
+ // Stop heartbeat when page unloads
113
+ window.addEventListener('beforeunload', () => {
114
+ if (heartbeatTimer) clearInterval(heartbeatTimer);
115
+ });
116
+
117
+ // Start heartbeat on page load
118
+ startHeartbeat();
102
119
 
103
120
  function extractContext(text) {
104
121
  const match = text.match(/(https?:\/\/[^/\s]+)\/project\/#\/team\/([^/\s]+)\/task\/([A-Za-z0-9]{10,})/);
@@ -1,34 +1,15 @@
1
1
  Set WshShell = CreateObject("WScript.Shell")
2
2
  Set fso = CreateObject("Scripting.FileSystemObject")
3
3
 
4
- ' 获取脚本所在目录
4
+ ' 获取项目根目录
5
5
  scriptDir = fso.GetParentFolderName(WScript.ScriptFullName)
6
-
7
- ' 检查 Node.js 是否安装
8
- On Error Resume Next
9
- WshShell.Run "node --version", 0, True
10
- If Err.Number <> 0 Then
11
- MsgBox "未检测到 Node.js,请先安装 Node.js 18 或更高版本。" & vbCrLf & vbCrLf & "下载地址:https://nodejs.org/", vbCritical, "ONES 采集工具"
12
- WScript.Quit
13
- End If
14
- On Error Goto 0
15
-
16
- ' 检查是否已安装依赖
17
- If Not fso.FolderExists(scriptDir & "\node_modules") Then
18
- result = MsgBox("首次运行需要安装依赖(约 30MB),是否继续?", vbYesNo + vbQuestion, "ONES 采集工具")
19
- If result = vbNo Then
20
- WScript.Quit
21
- End If
22
-
23
- ' 显示安装窗口
24
- WshShell.Run "cmd /c cd /d """ & scriptDir & """ && npm install && pause", 1, True
25
- End If
6
+ projectRoot = fso.GetParentFolderName(scriptDir)
26
7
 
27
8
  ' 启动服务器(后台运行)
28
- WshShell.Run "cmd /c cd /d """ & scriptDir & """ && node src/server.mjs", 0, False
9
+ WshShell.Run "cmd /c cd /d """ & projectRoot & """ && node src/server.mjs", 0, False
29
10
 
30
11
  ' 等待服务器启动
31
- WScript.Sleep 2000
12
+ WScript.Sleep 1500
32
13
 
33
14
  ' 打开浏览器
34
15
  WshShell.Run "http://localhost:3000", 1
package/src/server.mjs CHANGED
@@ -365,6 +365,21 @@ async function handleCrawl(req, res) {
365
365
  }
366
366
  }
367
367
 
368
+ // Heartbeat tracking
369
+ let lastHeartbeat = Date.now();
370
+ const HEARTBEAT_TIMEOUT = 2 * 60 * 1000; // 2 minutes
371
+ const HEARTBEAT_CHECK_INTERVAL = 30 * 1000; // 30 seconds
372
+
373
+ function startHeartbeatMonitor() {
374
+ setInterval(() => {
375
+ const now = Date.now();
376
+ if (now - lastHeartbeat > HEARTBEAT_TIMEOUT) {
377
+ process.stdout.write('No heartbeat received for 5 minutes, shutting down server...\n');
378
+ process.exit(0);
379
+ }
380
+ }, HEARTBEAT_CHECK_INTERVAL);
381
+ }
382
+
368
383
  const server = http.createServer(async (req, res) => {
369
384
  if (req.method === 'GET' && req.url === '/') {
370
385
  try {
@@ -376,6 +391,12 @@ const server = http.createServer(async (req, res) => {
376
391
  }
377
392
  return;
378
393
  }
394
+ if (req.method === 'GET' && req.url === '/api/heartbeat') {
395
+ lastHeartbeat = Date.now();
396
+ res.writeHead(204);
397
+ res.end();
398
+ return;
399
+ }
379
400
  if (req.method === 'GET' && req.url === '/api/auth/status') {
380
401
  return handleAuthStatus(req, res);
381
402
  }
@@ -392,4 +413,16 @@ server.listen(PORT, () => {
392
413
  const url = `http://localhost:${PORT}`;
393
414
  process.stdout.write(`ONES Fetch Web UI → ${url}\n`);
394
415
  openBrowser(url);
416
+ startHeartbeatMonitor();
417
+ });
418
+
419
+ server.on('error', (err) => {
420
+ if (err.code === 'EADDRINUSE') {
421
+ process.stdout.write(`Port ${PORT} is already in use. Opening browser to existing instance...\n`);
422
+ openBrowser(`http://localhost:${PORT}`);
423
+ process.exit(0);
424
+ } else {
425
+ process.stderr.write(`Server error: ${err.message}\n`);
426
+ process.exit(1);
427
+ }
395
428
  });
@@ -1,19 +0,0 @@
1
- {
2
- "permissions": {
3
- "allow": [
4
- "Bash(npm install:*)",
5
- "Bash(node:*)",
6
- "Bash(timeout 3 npm start)",
7
- "Bash(ls:*)",
8
- "Bash(python process_icon.py)",
9
- "Bash(python -c \"from PIL import Image; img = Image.open\\('public/icon.png'\\); print\\(f'尺寸: {img.size}, 模式: {img.mode}'\\)\")",
10
- "Bash(git add:*)",
11
- "Bash(git reset:*)",
12
- "Bash(git mv:*)",
13
- "Bash(gh --version)",
14
- "Bash(gh repo:*)",
15
- "Bash(npm whoami:*)",
16
- "Bash(npm login:*)"
17
- ]
18
- }
19
- }
package/public/icon.png DELETED
Binary file