@jackwener/opencli 1.6.10 → 1.7.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 +15 -2
- package/README.zh-CN.md +15 -2
- package/dist/clis/jianyu/detail.js +20 -0
- package/dist/clis/jianyu/search.d.ts +41 -4
- package/dist/clis/jianyu/search.js +458 -96
- package/dist/clis/jianyu/search.test.js +105 -0
- package/dist/clis/jianyu/shared/china-bid-search.d.ts +12 -0
- package/dist/clis/jianyu/shared/china-bid-search.js +165 -0
- package/dist/clis/jianyu/shared/procurement-contract.d.ts +68 -0
- package/dist/clis/jianyu/shared/procurement-contract.js +324 -0
- package/dist/clis/jianyu/shared/procurement-contract.test.d.ts +1 -0
- package/dist/clis/jianyu/shared/procurement-contract.test.js +72 -0
- package/dist/clis/jianyu/shared/procurement-detail.d.ts +6 -0
- package/dist/clis/jianyu/shared/procurement-detail.js +92 -0
- package/dist/clis/jianyu/shared/procurement-detail.test.d.ts +1 -0
- package/dist/clis/jianyu/shared/procurement-detail.test.js +72 -0
- package/dist/clis/xiaoe/catalog.js +36 -0
- package/dist/src/browser/bridge.js +1 -1
- package/dist/src/browser/daemon-client.d.ts +2 -1
- package/dist/src/browser/daemon-client.js +3 -1
- package/dist/src/browser/daemon-client.test.js +0 -3
- package/dist/src/browser.test.js +0 -1
- package/dist/src/cli.js +1 -9
- package/dist/src/commands/daemon.d.ts +2 -6
- package/dist/src/commands/daemon.js +2 -58
- package/dist/src/commands/daemon.test.js +24 -120
- package/dist/src/constants.d.ts +0 -2
- package/dist/src/constants.js +0 -2
- package/dist/src/daemon.d.ts +1 -1
- package/dist/src/daemon.js +2 -15
- package/dist/src/execution.js +5 -1
- package/package.json +2 -1
- package/dist/src/daemon.test.js +0 -65
- package/dist/src/idle-manager.d.ts +0 -19
- package/dist/src/idle-manager.js +0 -54
- /package/dist/{src/daemon.test.d.ts → clis/jianyu/detail.d.ts} +0 -0
package/dist/src/constants.js
CHANGED
|
@@ -3,8 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
/** Default daemon port for HTTP/WebSocket communication with browser extension */
|
|
5
5
|
export const DEFAULT_DAEMON_PORT = 19825;
|
|
6
|
-
/** Default idle timeout before daemon auto-exits (ms). Override via OPENCLI_DAEMON_TIMEOUT env var. */
|
|
7
|
-
export const DEFAULT_DAEMON_IDLE_TIMEOUT = 4 * 60 * 60 * 1000; // 4 hours
|
|
8
6
|
/** URL query params that are volatile/ephemeral and should be stripped from patterns */
|
|
9
7
|
export const VOLATILE_PARAMS = new Set([
|
|
10
8
|
'w_rid', 'wts', '_', 'callback', 'timestamp', 't', 'nonce', 'sign',
|
package/dist/src/daemon.d.ts
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*
|
|
16
16
|
* Lifecycle:
|
|
17
17
|
* - Auto-spawned by opencli on first browser command
|
|
18
|
-
* -
|
|
18
|
+
* - Persistent — stays alive until explicit shutdown, SIGTERM, or uninstall
|
|
19
19
|
* - Listens on localhost:19825
|
|
20
20
|
*/
|
|
21
21
|
export {};
|
package/dist/src/daemon.js
CHANGED
|
@@ -15,16 +15,14 @@
|
|
|
15
15
|
*
|
|
16
16
|
* Lifecycle:
|
|
17
17
|
* - Auto-spawned by opencli on first browser command
|
|
18
|
-
* -
|
|
18
|
+
* - Persistent — stays alive until explicit shutdown, SIGTERM, or uninstall
|
|
19
19
|
* - Listens on localhost:19825
|
|
20
20
|
*/
|
|
21
21
|
import { createServer } from 'node:http';
|
|
22
22
|
import { WebSocketServer, WebSocket } from 'ws';
|
|
23
|
-
import { DEFAULT_DAEMON_PORT
|
|
23
|
+
import { DEFAULT_DAEMON_PORT } from './constants.js';
|
|
24
24
|
import { EXIT_CODES } from './errors.js';
|
|
25
|
-
import { IdleManager } from './idle-manager.js';
|
|
26
25
|
const PORT = parseInt(process.env.OPENCLI_DAEMON_PORT ?? String(DEFAULT_DAEMON_PORT), 10);
|
|
27
|
-
const IDLE_TIMEOUT = Number(process.env.OPENCLI_DAEMON_TIMEOUT ?? DEFAULT_DAEMON_IDLE_TIMEOUT);
|
|
28
26
|
// ─── State ───────────────────────────────────────────────────────────
|
|
29
27
|
let extensionWs = null;
|
|
30
28
|
let extensionVersion = null;
|
|
@@ -36,11 +34,6 @@ function pushLog(entry) {
|
|
|
36
34
|
if (logBuffer.length > LOG_BUFFER_SIZE)
|
|
37
35
|
logBuffer.shift();
|
|
38
36
|
}
|
|
39
|
-
// ─── Idle auto-exit ──────────────────────────────────────────────────
|
|
40
|
-
const idleManager = new IdleManager(IDLE_TIMEOUT, () => {
|
|
41
|
-
console.error('[daemon] Idle timeout (no CLI requests + no Extension), shutting down');
|
|
42
|
-
process.exit(EXIT_CODES.SUCCESS);
|
|
43
|
-
});
|
|
44
37
|
// ─── HTTP Server ─────────────────────────────────────────────────────
|
|
45
38
|
const MAX_BODY = 1024 * 1024; // 1 MB — commands are tiny; this prevents OOM
|
|
46
39
|
function readBody(req) {
|
|
@@ -118,7 +111,6 @@ async function handleRequest(req, res) {
|
|
|
118
111
|
extensionConnected: extensionWs?.readyState === WebSocket.OPEN,
|
|
119
112
|
extensionVersion,
|
|
120
113
|
pending: pending.size,
|
|
121
|
-
lastCliRequestTime: idleManager.lastCliRequestTime,
|
|
122
114
|
memoryMB: Math.round(mem.rss / 1024 / 1024 * 10) / 10,
|
|
123
115
|
port: PORT,
|
|
124
116
|
});
|
|
@@ -144,7 +136,6 @@ async function handleRequest(req, res) {
|
|
|
144
136
|
return;
|
|
145
137
|
}
|
|
146
138
|
if (req.method === 'POST' && url === '/command') {
|
|
147
|
-
idleManager.onCliRequest();
|
|
148
139
|
try {
|
|
149
140
|
const body = JSON.parse(await readBody(req));
|
|
150
141
|
if (!body.id) {
|
|
@@ -196,7 +187,6 @@ wss.on('connection', (ws) => {
|
|
|
196
187
|
console.error('[daemon] Extension connected');
|
|
197
188
|
extensionWs = ws;
|
|
198
189
|
extensionVersion = null; // cleared until hello message arrives
|
|
199
|
-
idleManager.setExtensionConnected(true);
|
|
200
190
|
// ── Heartbeat: ping every 15s, close if 2 pongs missed ──
|
|
201
191
|
let missedPongs = 0;
|
|
202
192
|
const heartbeatInterval = setInterval(() => {
|
|
@@ -249,7 +239,6 @@ wss.on('connection', (ws) => {
|
|
|
249
239
|
if (extensionWs === ws) {
|
|
250
240
|
extensionWs = null;
|
|
251
241
|
extensionVersion = null;
|
|
252
|
-
idleManager.setExtensionConnected(false);
|
|
253
242
|
// Reject all pending requests since the extension is gone
|
|
254
243
|
for (const [id, p] of pending) {
|
|
255
244
|
clearTimeout(p.timer);
|
|
@@ -263,7 +252,6 @@ wss.on('connection', (ws) => {
|
|
|
263
252
|
if (extensionWs === ws) {
|
|
264
253
|
extensionWs = null;
|
|
265
254
|
extensionVersion = null;
|
|
266
|
-
idleManager.setExtensionConnected(false);
|
|
267
255
|
// Reject pending requests in case 'close' does not follow this 'error'
|
|
268
256
|
for (const [, p] of pending) {
|
|
269
257
|
clearTimeout(p.timer);
|
|
@@ -276,7 +264,6 @@ wss.on('connection', (ws) => {
|
|
|
276
264
|
// ─── Start ───────────────────────────────────────────────────────────
|
|
277
265
|
httpServer.listen(PORT, '127.0.0.1', () => {
|
|
278
266
|
console.error(`[daemon] Listening on http://127.0.0.1:${PORT}`);
|
|
279
|
-
idleManager.onCliRequest();
|
|
280
267
|
});
|
|
281
268
|
httpServer.on('error', (err) => {
|
|
282
269
|
if (err.code === 'EADDRINUSE') {
|
package/dist/src/execution.js
CHANGED
|
@@ -167,10 +167,14 @@ export async function executeCommand(cmd, rawKwargs, debug = false) {
|
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
169
|
try {
|
|
170
|
-
|
|
170
|
+
const result = await runWithTimeout(runCommand(cmd, page, kwargs, debug), {
|
|
171
171
|
timeout: cmd.timeoutSeconds ?? DEFAULT_BROWSER_COMMAND_TIMEOUT,
|
|
172
172
|
label: fullName(cmd),
|
|
173
173
|
});
|
|
174
|
+
// Adapter commands are one-shot — close the automation window immediately
|
|
175
|
+
// instead of waiting for the 30s idle timeout.
|
|
176
|
+
await page.closeWindow?.().catch(() => { });
|
|
177
|
+
return result;
|
|
174
178
|
}
|
|
175
179
|
catch (err) {
|
|
176
180
|
// Collect diagnostic while page is still alive (before browserSession closes it).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jackwener/opencli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
"copy-yaml": "node scripts/copy-yaml.cjs",
|
|
49
49
|
"start": "node dist/src/main.js",
|
|
50
50
|
"start:bun": "bun dist/src/main.js",
|
|
51
|
+
"preuninstall": "node -e \"fetch('http://127.0.0.1:19825/shutdown',{method:'POST',headers:{'X-OpenCLI':'1'},signal:AbortSignal.timeout(3000)}).catch(()=>{})\" || true",
|
|
51
52
|
"postinstall": "node scripts/postinstall.js || true; node scripts/fetch-adapters.js || true",
|
|
52
53
|
"typecheck": "tsc --noEmit",
|
|
53
54
|
"lint": "tsc --noEmit",
|
package/dist/src/daemon.test.js
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import { IdleManager } from './idle-manager.js';
|
|
3
|
-
describe('IdleManager', () => {
|
|
4
|
-
beforeEach(() => {
|
|
5
|
-
vi.useFakeTimers();
|
|
6
|
-
});
|
|
7
|
-
afterEach(() => {
|
|
8
|
-
vi.useRealTimers();
|
|
9
|
-
});
|
|
10
|
-
it('does not start timer when extension is connected', () => {
|
|
11
|
-
const exit = vi.fn();
|
|
12
|
-
const mgr = new IdleManager(300_000, exit);
|
|
13
|
-
mgr.setExtensionConnected(true);
|
|
14
|
-
mgr.onCliRequest();
|
|
15
|
-
vi.advanceTimersByTime(300_000 + 1000);
|
|
16
|
-
expect(exit).not.toHaveBeenCalled();
|
|
17
|
-
});
|
|
18
|
-
it('starts timer when extension disconnects and CLI is idle', () => {
|
|
19
|
-
const exit = vi.fn();
|
|
20
|
-
const mgr = new IdleManager(300_000, exit);
|
|
21
|
-
mgr.onCliRequest();
|
|
22
|
-
mgr.setExtensionConnected(true);
|
|
23
|
-
mgr.setExtensionConnected(false);
|
|
24
|
-
expect(exit).not.toHaveBeenCalled();
|
|
25
|
-
vi.advanceTimersByTime(300_000 + 1000);
|
|
26
|
-
expect(exit).toHaveBeenCalledTimes(1);
|
|
27
|
-
});
|
|
28
|
-
it('exits immediately on extension disconnect if CLI has been idle past timeout', () => {
|
|
29
|
-
const exit = vi.fn();
|
|
30
|
-
const mgr = new IdleManager(300_000, exit);
|
|
31
|
-
mgr.onCliRequest();
|
|
32
|
-
mgr.setExtensionConnected(true); // connect before timeout elapses
|
|
33
|
-
vi.advanceTimersByTime(400_000); // CLI idle time exceeds timeout, but extension is connected so no exit
|
|
34
|
-
expect(exit).not.toHaveBeenCalled();
|
|
35
|
-
mgr.setExtensionConnected(false); // disconnect → should exit immediately since CLI idle > timeout
|
|
36
|
-
expect(exit).toHaveBeenCalledTimes(1);
|
|
37
|
-
});
|
|
38
|
-
it('resets timer on new CLI request', () => {
|
|
39
|
-
const exit = vi.fn();
|
|
40
|
-
const mgr = new IdleManager(300_000, exit);
|
|
41
|
-
mgr.onCliRequest();
|
|
42
|
-
vi.advanceTimersByTime(200_000);
|
|
43
|
-
mgr.onCliRequest();
|
|
44
|
-
vi.advanceTimersByTime(200_000);
|
|
45
|
-
expect(exit).not.toHaveBeenCalled();
|
|
46
|
-
vi.advanceTimersByTime(100_001);
|
|
47
|
-
expect(exit).toHaveBeenCalledTimes(1);
|
|
48
|
-
});
|
|
49
|
-
it('does not exit when timeout is 0 (disabled)', () => {
|
|
50
|
-
const exit = vi.fn();
|
|
51
|
-
const mgr = new IdleManager(0, exit);
|
|
52
|
-
mgr.onCliRequest();
|
|
53
|
-
vi.advanceTimersByTime(24 * 60 * 60 * 1000);
|
|
54
|
-
expect(exit).not.toHaveBeenCalled();
|
|
55
|
-
});
|
|
56
|
-
it('clears timer when extension connects', () => {
|
|
57
|
-
const exit = vi.fn();
|
|
58
|
-
const mgr = new IdleManager(300_000, exit);
|
|
59
|
-
mgr.onCliRequest();
|
|
60
|
-
vi.advanceTimersByTime(200_000);
|
|
61
|
-
mgr.setExtensionConnected(true);
|
|
62
|
-
vi.advanceTimersByTime(200_000);
|
|
63
|
-
expect(exit).not.toHaveBeenCalled();
|
|
64
|
-
});
|
|
65
|
-
});
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Manages daemon idle timeout with dual-condition logic:
|
|
3
|
-
* exits only when BOTH CLI is idle AND Extension is disconnected.
|
|
4
|
-
*/
|
|
5
|
-
export declare class IdleManager {
|
|
6
|
-
private _timer;
|
|
7
|
-
private _lastCliRequestTime;
|
|
8
|
-
private _extensionConnected;
|
|
9
|
-
private _timeoutMs;
|
|
10
|
-
private _onExit;
|
|
11
|
-
constructor(timeoutMs: number, onExit: () => void);
|
|
12
|
-
get lastCliRequestTime(): number;
|
|
13
|
-
/** Call when an HTTP request arrives from CLI */
|
|
14
|
-
onCliRequest(): void;
|
|
15
|
-
/** Call when Extension WebSocket connects or disconnects */
|
|
16
|
-
setExtensionConnected(connected: boolean): void;
|
|
17
|
-
private _clearTimer;
|
|
18
|
-
private _resetTimer;
|
|
19
|
-
}
|
package/dist/src/idle-manager.js
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Manages daemon idle timeout with dual-condition logic:
|
|
3
|
-
* exits only when BOTH CLI is idle AND Extension is disconnected.
|
|
4
|
-
*/
|
|
5
|
-
export class IdleManager {
|
|
6
|
-
_timer = null;
|
|
7
|
-
_lastCliRequestTime = Date.now();
|
|
8
|
-
_extensionConnected = false;
|
|
9
|
-
_timeoutMs;
|
|
10
|
-
_onExit;
|
|
11
|
-
constructor(timeoutMs, onExit) {
|
|
12
|
-
this._timeoutMs = timeoutMs;
|
|
13
|
-
this._onExit = onExit;
|
|
14
|
-
}
|
|
15
|
-
get lastCliRequestTime() {
|
|
16
|
-
return this._lastCliRequestTime;
|
|
17
|
-
}
|
|
18
|
-
/** Call when an HTTP request arrives from CLI */
|
|
19
|
-
onCliRequest() {
|
|
20
|
-
this._lastCliRequestTime = Date.now();
|
|
21
|
-
this._resetTimer();
|
|
22
|
-
}
|
|
23
|
-
/** Call when Extension WebSocket connects or disconnects */
|
|
24
|
-
setExtensionConnected(connected) {
|
|
25
|
-
this._extensionConnected = connected;
|
|
26
|
-
if (connected) {
|
|
27
|
-
this._clearTimer();
|
|
28
|
-
}
|
|
29
|
-
else {
|
|
30
|
-
this._resetTimer();
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
_clearTimer() {
|
|
34
|
-
if (this._timer) {
|
|
35
|
-
clearTimeout(this._timer);
|
|
36
|
-
this._timer = null;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
_resetTimer() {
|
|
40
|
-
this._clearTimer();
|
|
41
|
-
if (this._timeoutMs <= 0)
|
|
42
|
-
return;
|
|
43
|
-
if (this._extensionConnected)
|
|
44
|
-
return;
|
|
45
|
-
const elapsed = Date.now() - this._lastCliRequestTime;
|
|
46
|
-
if (elapsed >= this._timeoutMs) {
|
|
47
|
-
this._onExit();
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
this._timer = setTimeout(() => {
|
|
51
|
-
this._onExit();
|
|
52
|
-
}, this._timeoutMs - elapsed);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
File without changes
|