chatccc 0.2.52 → 0.2.53
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 +95 -256
- package/bin/chatccc.mjs +23 -23
- package/demo/ilink_echo_probe.ts +222 -222
- package/im-skills/feishu-skill/download-video.mjs +162 -162
- package/im-skills/feishu-skill/receive-send-file.md +66 -66
- package/im-skills/feishu-skill/receive-send-image.md +27 -27
- package/im-skills/feishu-skill/send-file.mjs +50 -50
- package/im-skills/feishu-skill/send-image.mjs +50 -50
- package/im-skills/feishu-skill/skill.md +10 -10
- package/package.json +59 -59
- package/src/__tests__/agent-image-rpc.test.ts +33 -33
- package/src/__tests__/agent-rpc-body.test.ts +41 -41
- package/src/__tests__/card-plain-text.test.ts +42 -42
- package/src/__tests__/codex-adapter.test.ts +304 -304
- package/src/__tests__/config-reload.test.ts +26 -26
- package/src/__tests__/config-sample.test.ts +19 -19
- package/src/__tests__/crash-logging.test.ts +62 -62
- package/src/__tests__/fixtures/codex_simple_text.jsonl +4 -4
- package/src/__tests__/fixtures/codex_with_tool.jsonl +6 -6
- package/src/__tests__/im-skills.test.ts +46 -46
- package/src/__tests__/session.test.ts +57 -2
- package/src/__tests__/sim-agent.test.ts +173 -173
- package/src/__tests__/sim-platform.test.ts +76 -76
- package/src/__tests__/sim-store.test.ts +213 -213
- package/src/__tests__/stream-state.test.ts +134 -134
- package/src/__tests__/wechat-platform.test.ts +57 -32
- package/src/adapters/codex-adapter.ts +294 -294
- package/src/adapters/codex-session-meta-store.ts +130 -130
- package/src/agent-file-rpc.ts +156 -156
- package/src/agent-image-rpc.ts +152 -152
- package/src/agent-rpc-body.ts +48 -48
- package/src/card-plain-text.ts +108 -108
- package/src/im-skills.ts +109 -109
- package/src/platform-adapter.ts +60 -60
- package/src/session-chat-binding.ts +6 -1
- package/src/session.ts +40 -35
- package/src/sim-agent.ts +167 -167
- package/src/trace.ts +50 -50
- package/src/wechat-platform.ts +1 -1
package/demo/ilink_echo_probe.ts
CHANGED
|
@@ -1,222 +1,222 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* OpenILink WeChat Echo Probe
|
|
3
|
-
* ===========================
|
|
4
|
-
* Minimal scan-login and text echo demo for the WeChat iLink channel.
|
|
5
|
-
*
|
|
6
|
-
* Usage:
|
|
7
|
-
* npm run demo:ilink-echo
|
|
8
|
-
*
|
|
9
|
-
* Behavior:
|
|
10
|
-
* When a user sends text to the logged-in WeChat account, this demo replies
|
|
11
|
-
* with "收到:" plus the received text.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
15
|
-
import { createRequire } from "node:module";
|
|
16
|
-
import { dirname, join } from "node:path";
|
|
17
|
-
import { fileURLToPath } from "node:url";
|
|
18
|
-
|
|
19
|
-
import {
|
|
20
|
-
Client as OpenIlinkWire,
|
|
21
|
-
extractText,
|
|
22
|
-
type GetUpdatesResponse,
|
|
23
|
-
type WeixinMessage,
|
|
24
|
-
} from "@openilink/openilink-sdk-node";
|
|
25
|
-
|
|
26
|
-
import { setupFileLogging } from "../src/shared.ts";
|
|
27
|
-
|
|
28
|
-
interface TerminalQrRenderer {
|
|
29
|
-
generate(
|
|
30
|
-
input: string,
|
|
31
|
-
opts: { small: boolean },
|
|
32
|
-
callback: (qrcode: string) => void,
|
|
33
|
-
): void;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
interface EchoProbeSnapshot {
|
|
37
|
-
token?: string;
|
|
38
|
-
baseUrl?: string;
|
|
39
|
-
pollCursor?: string;
|
|
40
|
-
botId?: string;
|
|
41
|
-
userId?: string;
|
|
42
|
-
lastSeenAt?: string;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
46
|
-
const PROJECT_ROOT = join(__dirname, "..");
|
|
47
|
-
const SNAPSHOT_PATH = join(PROJECT_ROOT, "state", "ilink-echo-probe.json");
|
|
48
|
-
const LOG_DIR = join(__dirname, "logs");
|
|
49
|
-
const requireFromDemo = createRequire(import.meta.url);
|
|
50
|
-
const terminalQr = requireFromDemo("qrcode-terminal") as TerminalQrRenderer;
|
|
51
|
-
|
|
52
|
-
setupFileLogging(LOG_DIR, "ilink-echo-probe");
|
|
53
|
-
|
|
54
|
-
let acceptingEvents = true;
|
|
55
|
-
|
|
56
|
-
function ensureParentDir(filePath: string): void {
|
|
57
|
-
mkdirSync(dirname(filePath), { recursive: true });
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function readSnapshot(): EchoProbeSnapshot {
|
|
61
|
-
if (!existsSync(SNAPSHOT_PATH)) {
|
|
62
|
-
return {};
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
try {
|
|
66
|
-
return JSON.parse(readFileSync(SNAPSHOT_PATH, "utf8")) as EchoProbeSnapshot;
|
|
67
|
-
} catch (error) {
|
|
68
|
-
console.warn(`Cannot read saved iLink probe state: ${(error as Error).message}`);
|
|
69
|
-
return {};
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function writeSnapshot(snapshot: EchoProbeSnapshot): void {
|
|
74
|
-
ensureParentDir(SNAPSHOT_PATH);
|
|
75
|
-
writeFileSync(SNAPSHOT_PATH, `${JSON.stringify(snapshot, null, 2)}\n`);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function updateSnapshot(patch: Partial<EchoProbeSnapshot>): EchoProbeSnapshot {
|
|
79
|
-
const next = {
|
|
80
|
-
...readSnapshot(),
|
|
81
|
-
...patch,
|
|
82
|
-
lastSeenAt: new Date().toISOString(),
|
|
83
|
-
};
|
|
84
|
-
writeSnapshot(next);
|
|
85
|
-
return next;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function peerKeyOf(message: WeixinMessage): string {
|
|
89
|
-
return String(message.from_user_id ?? "");
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function textOf(message: WeixinMessage): string {
|
|
93
|
-
return extractText(message).trim();
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function printScanMaterial(content: string): void {
|
|
97
|
-
console.log("\n========== iLink scan content ==========");
|
|
98
|
-
console.log(content);
|
|
99
|
-
console.log("========== iLink terminal QR ==========");
|
|
100
|
-
terminalQr.generate(content, { small: true }, (renderedQr) => {
|
|
101
|
-
console.log(renderedQr);
|
|
102
|
-
});
|
|
103
|
-
console.log("========== end iLink QR ==========\n");
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
async function prepareProbeWire(saved: EchoProbeSnapshot): Promise<OpenIlinkWire> {
|
|
107
|
-
const ilinkWire = new OpenIlinkWire("", {
|
|
108
|
-
base_url: saved.baseUrl,
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
console.log("Starting iLink QR login. The QR is shown on every run.");
|
|
112
|
-
const loginResult = await ilinkWire.loginWithQr({
|
|
113
|
-
on_qrcode: (content) => {
|
|
114
|
-
printScanMaterial(content);
|
|
115
|
-
},
|
|
116
|
-
on_scanned: () => {
|
|
117
|
-
console.log("QR scanned. Confirm login in WeChat.");
|
|
118
|
-
},
|
|
119
|
-
on_expired: (attempt, maxAttempts) => {
|
|
120
|
-
console.log(`QR expired, refreshing (${attempt}/${maxAttempts}).`);
|
|
121
|
-
},
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
if (!loginResult.connected) {
|
|
125
|
-
throw new Error(`iLink QR login failed: ${loginResult.message}`);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
updateSnapshot({
|
|
129
|
-
token: loginResult.bot_token ?? ilinkWire.token,
|
|
130
|
-
baseUrl: loginResult.base_url ?? ilinkWire.baseUrl,
|
|
131
|
-
botId: loginResult.bot_id,
|
|
132
|
-
userId: loginResult.user_id,
|
|
133
|
-
pollCursor: "",
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
console.log(`iLink login ready. BotID=${loginResult.bot_id ?? ""}`);
|
|
137
|
-
return ilinkWire;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
async function mirrorIncomingLine(
|
|
141
|
-
ilinkWire: OpenIlinkWire,
|
|
142
|
-
message: WeixinMessage,
|
|
143
|
-
): Promise<void> {
|
|
144
|
-
const peerKey = peerKeyOf(message);
|
|
145
|
-
if (!peerKey) {
|
|
146
|
-
console.warn("Skip message without from_user_id.");
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const incomingText = textOf(message);
|
|
151
|
-
const outgoingText = incomingText ? `收到:${incomingText}` : "收到:[non-text message]";
|
|
152
|
-
const contextToken = message.context_token;
|
|
153
|
-
|
|
154
|
-
if (contextToken) {
|
|
155
|
-
await ilinkWire.sendText(peerKey, outgoingText, contextToken);
|
|
156
|
-
} else {
|
|
157
|
-
await ilinkWire.push(peerKey, outgoingText);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
console.log(`Echoed to ${peerKey}: ${outgoingText.slice(0, 120)}`);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
function rememberPollingCursor(response: GetUpdatesResponse): void {
|
|
164
|
-
const pollCursor = response.sync_buf ?? response.get_updates_buf;
|
|
165
|
-
if (!pollCursor) {
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
updateSnapshot({ pollCursor });
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
function installStopHooks(): void {
|
|
172
|
-
const stop = (): void => {
|
|
173
|
-
if (!acceptingEvents) {
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
acceptingEvents = false;
|
|
177
|
-
console.log("Stopping iLink echo probe...");
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
process.once("SIGINT", stop);
|
|
181
|
-
process.once("SIGTERM", stop);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
async function runProbe(): Promise<void> {
|
|
185
|
-
installStopHooks();
|
|
186
|
-
|
|
187
|
-
const saved = readSnapshot();
|
|
188
|
-
const ilinkWire = await prepareProbeWire(saved);
|
|
189
|
-
const latest = readSnapshot();
|
|
190
|
-
|
|
191
|
-
console.log("Listening for WeChat messages. Send text to the small account.");
|
|
192
|
-
console.log(`State file: ${SNAPSHOT_PATH}`);
|
|
193
|
-
console.log(`Log dir: ${LOG_DIR}`);
|
|
194
|
-
|
|
195
|
-
await ilinkWire.monitor(
|
|
196
|
-
async (message) => {
|
|
197
|
-
try {
|
|
198
|
-
await mirrorIncomingLine(ilinkWire, message);
|
|
199
|
-
} catch (error) {
|
|
200
|
-
console.error(`Echo failed: ${(error as Error).stack ?? (error as Error).message}`);
|
|
201
|
-
}
|
|
202
|
-
},
|
|
203
|
-
{
|
|
204
|
-
initial_buf: latest.pollCursor ?? "",
|
|
205
|
-
on_buf_update: (pollCursor) => updateSnapshot({ pollCursor }),
|
|
206
|
-
on_response: rememberPollingCursor,
|
|
207
|
-
on_error: (error) => {
|
|
208
|
-
console.error(`iLink monitor error: ${error.stack ?? error.message}`);
|
|
209
|
-
},
|
|
210
|
-
on_session_expired: () => {
|
|
211
|
-
console.error("iLink session expired. Restart with --fresh to scan again.");
|
|
212
|
-
acceptingEvents = false;
|
|
213
|
-
},
|
|
214
|
-
should_continue: () => acceptingEvents,
|
|
215
|
-
},
|
|
216
|
-
);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
runProbe().catch((error: Error) => {
|
|
220
|
-
console.error(`Fatal iLink echo probe error: ${error.stack ?? error.message}`);
|
|
221
|
-
process.exitCode = 1;
|
|
222
|
-
});
|
|
1
|
+
/**
|
|
2
|
+
* OpenILink WeChat Echo Probe
|
|
3
|
+
* ===========================
|
|
4
|
+
* Minimal scan-login and text echo demo for the WeChat iLink channel.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* npm run demo:ilink-echo
|
|
8
|
+
*
|
|
9
|
+
* Behavior:
|
|
10
|
+
* When a user sends text to the logged-in WeChat account, this demo replies
|
|
11
|
+
* with "收到:" plus the received text.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
15
|
+
import { createRequire } from "node:module";
|
|
16
|
+
import { dirname, join } from "node:path";
|
|
17
|
+
import { fileURLToPath } from "node:url";
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
Client as OpenIlinkWire,
|
|
21
|
+
extractText,
|
|
22
|
+
type GetUpdatesResponse,
|
|
23
|
+
type WeixinMessage,
|
|
24
|
+
} from "@openilink/openilink-sdk-node";
|
|
25
|
+
|
|
26
|
+
import { setupFileLogging } from "../src/shared.ts";
|
|
27
|
+
|
|
28
|
+
interface TerminalQrRenderer {
|
|
29
|
+
generate(
|
|
30
|
+
input: string,
|
|
31
|
+
opts: { small: boolean },
|
|
32
|
+
callback: (qrcode: string) => void,
|
|
33
|
+
): void;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface EchoProbeSnapshot {
|
|
37
|
+
token?: string;
|
|
38
|
+
baseUrl?: string;
|
|
39
|
+
pollCursor?: string;
|
|
40
|
+
botId?: string;
|
|
41
|
+
userId?: string;
|
|
42
|
+
lastSeenAt?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
46
|
+
const PROJECT_ROOT = join(__dirname, "..");
|
|
47
|
+
const SNAPSHOT_PATH = join(PROJECT_ROOT, "state", "ilink-echo-probe.json");
|
|
48
|
+
const LOG_DIR = join(__dirname, "logs");
|
|
49
|
+
const requireFromDemo = createRequire(import.meta.url);
|
|
50
|
+
const terminalQr = requireFromDemo("qrcode-terminal") as TerminalQrRenderer;
|
|
51
|
+
|
|
52
|
+
setupFileLogging(LOG_DIR, "ilink-echo-probe");
|
|
53
|
+
|
|
54
|
+
let acceptingEvents = true;
|
|
55
|
+
|
|
56
|
+
function ensureParentDir(filePath: string): void {
|
|
57
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function readSnapshot(): EchoProbeSnapshot {
|
|
61
|
+
if (!existsSync(SNAPSHOT_PATH)) {
|
|
62
|
+
return {};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
return JSON.parse(readFileSync(SNAPSHOT_PATH, "utf8")) as EchoProbeSnapshot;
|
|
67
|
+
} catch (error) {
|
|
68
|
+
console.warn(`Cannot read saved iLink probe state: ${(error as Error).message}`);
|
|
69
|
+
return {};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function writeSnapshot(snapshot: EchoProbeSnapshot): void {
|
|
74
|
+
ensureParentDir(SNAPSHOT_PATH);
|
|
75
|
+
writeFileSync(SNAPSHOT_PATH, `${JSON.stringify(snapshot, null, 2)}\n`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function updateSnapshot(patch: Partial<EchoProbeSnapshot>): EchoProbeSnapshot {
|
|
79
|
+
const next = {
|
|
80
|
+
...readSnapshot(),
|
|
81
|
+
...patch,
|
|
82
|
+
lastSeenAt: new Date().toISOString(),
|
|
83
|
+
};
|
|
84
|
+
writeSnapshot(next);
|
|
85
|
+
return next;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function peerKeyOf(message: WeixinMessage): string {
|
|
89
|
+
return String(message.from_user_id ?? "");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function textOf(message: WeixinMessage): string {
|
|
93
|
+
return extractText(message).trim();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function printScanMaterial(content: string): void {
|
|
97
|
+
console.log("\n========== iLink scan content ==========");
|
|
98
|
+
console.log(content);
|
|
99
|
+
console.log("========== iLink terminal QR ==========");
|
|
100
|
+
terminalQr.generate(content, { small: true }, (renderedQr) => {
|
|
101
|
+
console.log(renderedQr);
|
|
102
|
+
});
|
|
103
|
+
console.log("========== end iLink QR ==========\n");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function prepareProbeWire(saved: EchoProbeSnapshot): Promise<OpenIlinkWire> {
|
|
107
|
+
const ilinkWire = new OpenIlinkWire("", {
|
|
108
|
+
base_url: saved.baseUrl,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
console.log("Starting iLink QR login. The QR is shown on every run.");
|
|
112
|
+
const loginResult = await ilinkWire.loginWithQr({
|
|
113
|
+
on_qrcode: (content) => {
|
|
114
|
+
printScanMaterial(content);
|
|
115
|
+
},
|
|
116
|
+
on_scanned: () => {
|
|
117
|
+
console.log("QR scanned. Confirm login in WeChat.");
|
|
118
|
+
},
|
|
119
|
+
on_expired: (attempt, maxAttempts) => {
|
|
120
|
+
console.log(`QR expired, refreshing (${attempt}/${maxAttempts}).`);
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
if (!loginResult.connected) {
|
|
125
|
+
throw new Error(`iLink QR login failed: ${loginResult.message}`);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
updateSnapshot({
|
|
129
|
+
token: loginResult.bot_token ?? ilinkWire.token,
|
|
130
|
+
baseUrl: loginResult.base_url ?? ilinkWire.baseUrl,
|
|
131
|
+
botId: loginResult.bot_id,
|
|
132
|
+
userId: loginResult.user_id,
|
|
133
|
+
pollCursor: "",
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
console.log(`iLink login ready. BotID=${loginResult.bot_id ?? ""}`);
|
|
137
|
+
return ilinkWire;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async function mirrorIncomingLine(
|
|
141
|
+
ilinkWire: OpenIlinkWire,
|
|
142
|
+
message: WeixinMessage,
|
|
143
|
+
): Promise<void> {
|
|
144
|
+
const peerKey = peerKeyOf(message);
|
|
145
|
+
if (!peerKey) {
|
|
146
|
+
console.warn("Skip message without from_user_id.");
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const incomingText = textOf(message);
|
|
151
|
+
const outgoingText = incomingText ? `收到:${incomingText}` : "收到:[non-text message]";
|
|
152
|
+
const contextToken = message.context_token;
|
|
153
|
+
|
|
154
|
+
if (contextToken) {
|
|
155
|
+
await ilinkWire.sendText(peerKey, outgoingText, contextToken);
|
|
156
|
+
} else {
|
|
157
|
+
await ilinkWire.push(peerKey, outgoingText);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
console.log(`Echoed to ${peerKey}: ${outgoingText.slice(0, 120)}`);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function rememberPollingCursor(response: GetUpdatesResponse): void {
|
|
164
|
+
const pollCursor = response.sync_buf ?? response.get_updates_buf;
|
|
165
|
+
if (!pollCursor) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
updateSnapshot({ pollCursor });
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function installStopHooks(): void {
|
|
172
|
+
const stop = (): void => {
|
|
173
|
+
if (!acceptingEvents) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
acceptingEvents = false;
|
|
177
|
+
console.log("Stopping iLink echo probe...");
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
process.once("SIGINT", stop);
|
|
181
|
+
process.once("SIGTERM", stop);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async function runProbe(): Promise<void> {
|
|
185
|
+
installStopHooks();
|
|
186
|
+
|
|
187
|
+
const saved = readSnapshot();
|
|
188
|
+
const ilinkWire = await prepareProbeWire(saved);
|
|
189
|
+
const latest = readSnapshot();
|
|
190
|
+
|
|
191
|
+
console.log("Listening for WeChat messages. Send text to the small account.");
|
|
192
|
+
console.log(`State file: ${SNAPSHOT_PATH}`);
|
|
193
|
+
console.log(`Log dir: ${LOG_DIR}`);
|
|
194
|
+
|
|
195
|
+
await ilinkWire.monitor(
|
|
196
|
+
async (message) => {
|
|
197
|
+
try {
|
|
198
|
+
await mirrorIncomingLine(ilinkWire, message);
|
|
199
|
+
} catch (error) {
|
|
200
|
+
console.error(`Echo failed: ${(error as Error).stack ?? (error as Error).message}`);
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
initial_buf: latest.pollCursor ?? "",
|
|
205
|
+
on_buf_update: (pollCursor) => updateSnapshot({ pollCursor }),
|
|
206
|
+
on_response: rememberPollingCursor,
|
|
207
|
+
on_error: (error) => {
|
|
208
|
+
console.error(`iLink monitor error: ${error.stack ?? error.message}`);
|
|
209
|
+
},
|
|
210
|
+
on_session_expired: () => {
|
|
211
|
+
console.error("iLink session expired. Restart with --fresh to scan again.");
|
|
212
|
+
acceptingEvents = false;
|
|
213
|
+
},
|
|
214
|
+
should_continue: () => acceptingEvents,
|
|
215
|
+
},
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
runProbe().catch((error: Error) => {
|
|
220
|
+
console.error(`Fatal iLink echo probe error: ${error.stack ?? error.message}`);
|
|
221
|
+
process.exitCode = 1;
|
|
222
|
+
});
|