pty-manager 1.6.8 → 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/dist/index.d.mts +26 -1
- package/dist/index.d.ts +26 -1
- package/dist/index.js +88 -47
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +88 -47
- package/dist/index.mjs.map +1 -1
- package/dist/pty-worker.js +87 -47
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -48,6 +48,10 @@ interface SpawnConfig {
|
|
|
48
48
|
* - null value disables that rule entirely
|
|
49
49
|
* - Object value merges fields into the matching adapter rule */
|
|
50
50
|
ruleOverrides?: Record<string, Partial<Omit<AutoResponseRule, 'pattern'>> | null>;
|
|
51
|
+
/** When true, adapter detectBlockingPrompt() results with suggestedResponse
|
|
52
|
+
* are emitted as autoResponded=false instead of being auto-responded.
|
|
53
|
+
* Auto-response rules (ruleOverrides) are unaffected. */
|
|
54
|
+
skipAdapterAutoResponse?: boolean;
|
|
51
55
|
}
|
|
52
56
|
/**
|
|
53
57
|
* Handle to a running session
|
|
@@ -99,8 +103,23 @@ interface LoginDetection {
|
|
|
99
103
|
required: boolean;
|
|
100
104
|
type?: 'api_key' | 'oauth' | 'browser' | 'device_code';
|
|
101
105
|
url?: string;
|
|
106
|
+
deviceCode?: string;
|
|
102
107
|
instructions?: string;
|
|
103
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* Normalized authentication methods for runtime event consumers.
|
|
111
|
+
*/
|
|
112
|
+
type AuthRequiredMethod = 'api_key' | 'oauth_browser' | 'device_code' | 'unknown';
|
|
113
|
+
/**
|
|
114
|
+
* Structured authentication-required payload emitted by PTY session/manager.
|
|
115
|
+
*/
|
|
116
|
+
interface AuthRequiredInfo {
|
|
117
|
+
method: AuthRequiredMethod;
|
|
118
|
+
url?: string;
|
|
119
|
+
deviceCode?: string;
|
|
120
|
+
instructions?: string;
|
|
121
|
+
promptSnippet?: string;
|
|
122
|
+
}
|
|
104
123
|
/**
|
|
105
124
|
* Types of blocking prompts that can occur
|
|
106
125
|
*/
|
|
@@ -425,6 +444,7 @@ interface PTYSessionEvents {
|
|
|
425
444
|
output: (data: string) => void;
|
|
426
445
|
ready: () => void;
|
|
427
446
|
login_required: (instructions?: string, url?: string) => void;
|
|
447
|
+
auth_required: (info: AuthRequiredInfo) => void;
|
|
428
448
|
blocking_prompt: (prompt: BlockingPromptInfo, autoResponded: boolean) => void;
|
|
429
449
|
message: (message: SessionMessage) => void;
|
|
430
450
|
question: (question: string) => void;
|
|
@@ -527,6 +547,10 @@ declare class PTYSession extends EventEmitter {
|
|
|
527
547
|
* Simple string hash for deduplication.
|
|
528
548
|
*/
|
|
529
549
|
private simpleHash;
|
|
550
|
+
private mapLoginTypeToAuthMethod;
|
|
551
|
+
private extractDeviceCode;
|
|
552
|
+
private getPromptSnippet;
|
|
553
|
+
private emitAuthRequired;
|
|
530
554
|
/**
|
|
531
555
|
* Strip ANSI codes, cursor movement, box-drawing, and spinner characters.
|
|
532
556
|
* Used for stall detection hashing and auto-response pattern matching.
|
|
@@ -697,6 +721,7 @@ interface PTYManagerEvents {
|
|
|
697
721
|
session_stopped: (session: SessionHandle, reason: string) => void;
|
|
698
722
|
session_error: (session: SessionHandle, error: string) => void;
|
|
699
723
|
login_required: (session: SessionHandle, instructions?: string, url?: string) => void;
|
|
724
|
+
auth_required: (session: SessionHandle, info: AuthRequiredInfo) => void;
|
|
700
725
|
blocking_prompt: (session: SessionHandle, promptInfo: BlockingPromptInfo, autoResponded: boolean) => void;
|
|
701
726
|
message: (message: SessionMessage) => void;
|
|
702
727
|
question: (session: SessionHandle, question: string) => void;
|
|
@@ -1156,4 +1181,4 @@ declare function isBun(): boolean;
|
|
|
1156
1181
|
*/
|
|
1157
1182
|
declare function createPTYManager(options?: BunPTYManagerOptions): BunCompatiblePTYManager;
|
|
1158
1183
|
|
|
1159
|
-
export { type AdapterFactoryConfig, AdapterRegistry, type AutoResponseRule, BaseCLIAdapter, type BlockingPromptDetection, type BlockingPromptInfo, type BlockingPromptType, type BuildTimelineOptions, BunCompatiblePTYManager, type BunPTYManagerOptions, type CLIAdapter, type LogOptions, type Logger, type LoginDetection, type MessageType, PTYManager, type PTYManagerConfig, type PTYManagerEvents, PTYSession, type PTYSessionEvents, type ParsedOutput, SPECIAL_KEYS, type SessionFilter, type SessionHandle, type SessionMessage, type SessionStatus, ShellAdapter, type ShellAdapterOptions, type SpawnConfig, type StallClassification, type StopOptions, type TaskCompletionTimelineResult, type TaskCompletionTimelineStep, type TaskCompletionTraceRecord, type TaskCompletionTurnTimeline, type TerminalAttachment, type WorkerSessionHandle, buildTaskCompletionTimeline, createAdapter, createPTYManager, extractTaskCompletionTraceRecords, isBun };
|
|
1184
|
+
export { type AdapterFactoryConfig, AdapterRegistry, type AuthRequiredInfo, type AuthRequiredMethod, type AutoResponseRule, BaseCLIAdapter, type BlockingPromptDetection, type BlockingPromptInfo, type BlockingPromptType, type BuildTimelineOptions, BunCompatiblePTYManager, type BunPTYManagerOptions, type CLIAdapter, type LogOptions, type Logger, type LoginDetection, type MessageType, PTYManager, type PTYManagerConfig, type PTYManagerEvents, PTYSession, type PTYSessionEvents, type ParsedOutput, SPECIAL_KEYS, type SessionFilter, type SessionHandle, type SessionMessage, type SessionStatus, ShellAdapter, type ShellAdapterOptions, type SpawnConfig, type StallClassification, type StopOptions, type TaskCompletionTimelineResult, type TaskCompletionTimelineStep, type TaskCompletionTraceRecord, type TaskCompletionTurnTimeline, type TerminalAttachment, type WorkerSessionHandle, buildTaskCompletionTimeline, createAdapter, createPTYManager, extractTaskCompletionTraceRecords, isBun };
|
package/dist/index.d.ts
CHANGED
|
@@ -48,6 +48,10 @@ interface SpawnConfig {
|
|
|
48
48
|
* - null value disables that rule entirely
|
|
49
49
|
* - Object value merges fields into the matching adapter rule */
|
|
50
50
|
ruleOverrides?: Record<string, Partial<Omit<AutoResponseRule, 'pattern'>> | null>;
|
|
51
|
+
/** When true, adapter detectBlockingPrompt() results with suggestedResponse
|
|
52
|
+
* are emitted as autoResponded=false instead of being auto-responded.
|
|
53
|
+
* Auto-response rules (ruleOverrides) are unaffected. */
|
|
54
|
+
skipAdapterAutoResponse?: boolean;
|
|
51
55
|
}
|
|
52
56
|
/**
|
|
53
57
|
* Handle to a running session
|
|
@@ -99,8 +103,23 @@ interface LoginDetection {
|
|
|
99
103
|
required: boolean;
|
|
100
104
|
type?: 'api_key' | 'oauth' | 'browser' | 'device_code';
|
|
101
105
|
url?: string;
|
|
106
|
+
deviceCode?: string;
|
|
102
107
|
instructions?: string;
|
|
103
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* Normalized authentication methods for runtime event consumers.
|
|
111
|
+
*/
|
|
112
|
+
type AuthRequiredMethod = 'api_key' | 'oauth_browser' | 'device_code' | 'unknown';
|
|
113
|
+
/**
|
|
114
|
+
* Structured authentication-required payload emitted by PTY session/manager.
|
|
115
|
+
*/
|
|
116
|
+
interface AuthRequiredInfo {
|
|
117
|
+
method: AuthRequiredMethod;
|
|
118
|
+
url?: string;
|
|
119
|
+
deviceCode?: string;
|
|
120
|
+
instructions?: string;
|
|
121
|
+
promptSnippet?: string;
|
|
122
|
+
}
|
|
104
123
|
/**
|
|
105
124
|
* Types of blocking prompts that can occur
|
|
106
125
|
*/
|
|
@@ -425,6 +444,7 @@ interface PTYSessionEvents {
|
|
|
425
444
|
output: (data: string) => void;
|
|
426
445
|
ready: () => void;
|
|
427
446
|
login_required: (instructions?: string, url?: string) => void;
|
|
447
|
+
auth_required: (info: AuthRequiredInfo) => void;
|
|
428
448
|
blocking_prompt: (prompt: BlockingPromptInfo, autoResponded: boolean) => void;
|
|
429
449
|
message: (message: SessionMessage) => void;
|
|
430
450
|
question: (question: string) => void;
|
|
@@ -527,6 +547,10 @@ declare class PTYSession extends EventEmitter {
|
|
|
527
547
|
* Simple string hash for deduplication.
|
|
528
548
|
*/
|
|
529
549
|
private simpleHash;
|
|
550
|
+
private mapLoginTypeToAuthMethod;
|
|
551
|
+
private extractDeviceCode;
|
|
552
|
+
private getPromptSnippet;
|
|
553
|
+
private emitAuthRequired;
|
|
530
554
|
/**
|
|
531
555
|
* Strip ANSI codes, cursor movement, box-drawing, and spinner characters.
|
|
532
556
|
* Used for stall detection hashing and auto-response pattern matching.
|
|
@@ -697,6 +721,7 @@ interface PTYManagerEvents {
|
|
|
697
721
|
session_stopped: (session: SessionHandle, reason: string) => void;
|
|
698
722
|
session_error: (session: SessionHandle, error: string) => void;
|
|
699
723
|
login_required: (session: SessionHandle, instructions?: string, url?: string) => void;
|
|
724
|
+
auth_required: (session: SessionHandle, info: AuthRequiredInfo) => void;
|
|
700
725
|
blocking_prompt: (session: SessionHandle, promptInfo: BlockingPromptInfo, autoResponded: boolean) => void;
|
|
701
726
|
message: (message: SessionMessage) => void;
|
|
702
727
|
question: (session: SessionHandle, question: string) => void;
|
|
@@ -1156,4 +1181,4 @@ declare function isBun(): boolean;
|
|
|
1156
1181
|
*/
|
|
1157
1182
|
declare function createPTYManager(options?: BunPTYManagerOptions): BunCompatiblePTYManager;
|
|
1158
1183
|
|
|
1159
|
-
export { type AdapterFactoryConfig, AdapterRegistry, type AutoResponseRule, BaseCLIAdapter, type BlockingPromptDetection, type BlockingPromptInfo, type BlockingPromptType, type BuildTimelineOptions, BunCompatiblePTYManager, type BunPTYManagerOptions, type CLIAdapter, type LogOptions, type Logger, type LoginDetection, type MessageType, PTYManager, type PTYManagerConfig, type PTYManagerEvents, PTYSession, type PTYSessionEvents, type ParsedOutput, SPECIAL_KEYS, type SessionFilter, type SessionHandle, type SessionMessage, type SessionStatus, ShellAdapter, type ShellAdapterOptions, type SpawnConfig, type StallClassification, type StopOptions, type TaskCompletionTimelineResult, type TaskCompletionTimelineStep, type TaskCompletionTraceRecord, type TaskCompletionTurnTimeline, type TerminalAttachment, type WorkerSessionHandle, buildTaskCompletionTimeline, createAdapter, createPTYManager, extractTaskCompletionTraceRecords, isBun };
|
|
1184
|
+
export { type AdapterFactoryConfig, AdapterRegistry, type AuthRequiredInfo, type AuthRequiredMethod, type AutoResponseRule, BaseCLIAdapter, type BlockingPromptDetection, type BlockingPromptInfo, type BlockingPromptType, type BuildTimelineOptions, BunCompatiblePTYManager, type BunPTYManagerOptions, type CLIAdapter, type LogOptions, type Logger, type LoginDetection, type MessageType, PTYManager, type PTYManagerConfig, type PTYManagerEvents, PTYSession, type PTYSessionEvents, type ParsedOutput, SPECIAL_KEYS, type SessionFilter, type SessionHandle, type SessionMessage, type SessionStatus, ShellAdapter, type ShellAdapterOptions, type SpawnConfig, type StallClassification, type StopOptions, type TaskCompletionTimelineResult, type TaskCompletionTimelineStep, type TaskCompletionTraceRecord, type TaskCompletionTurnTimeline, type TerminalAttachment, type WorkerSessionHandle, buildTaskCompletionTimeline, createAdapter, createPTYManager, extractTaskCompletionTraceRecords, isBun };
|
package/dist/index.js
CHANGED
|
@@ -98,19 +98,8 @@ var AdapterRegistry = class {
|
|
|
98
98
|
// src/pty-session.ts
|
|
99
99
|
var import_events = require("events");
|
|
100
100
|
var import_crypto = require("crypto");
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
if (!ptyCache) {
|
|
104
|
-
try {
|
|
105
|
-
ptyCache = require("node-pty");
|
|
106
|
-
} catch {
|
|
107
|
-
throw new Error(
|
|
108
|
-
"node-pty is required but not installed. Run: npm install node-pty"
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
return ptyCache;
|
|
113
|
-
}
|
|
101
|
+
|
|
102
|
+
// src/logger.ts
|
|
114
103
|
var consoleLogger = {
|
|
115
104
|
debug: (...args) => {
|
|
116
105
|
if (typeof args[0] === "string") {
|
|
@@ -141,6 +130,21 @@ var consoleLogger = {
|
|
|
141
130
|
}
|
|
142
131
|
}
|
|
143
132
|
};
|
|
133
|
+
|
|
134
|
+
// src/pty-session.ts
|
|
135
|
+
var ptyCache = null;
|
|
136
|
+
function loadPty() {
|
|
137
|
+
if (!ptyCache) {
|
|
138
|
+
try {
|
|
139
|
+
ptyCache = require("node-pty");
|
|
140
|
+
} catch {
|
|
141
|
+
throw new Error(
|
|
142
|
+
"node-pty is required but not installed. Run: npm install node-pty"
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return ptyCache;
|
|
147
|
+
}
|
|
144
148
|
function generateId() {
|
|
145
149
|
return `pty-${Date.now()}-${(0, import_crypto.randomUUID)().slice(0, 8)}`;
|
|
146
150
|
}
|
|
@@ -575,6 +579,51 @@ var PTYSession = class _PTYSession extends import_events.EventEmitter {
|
|
|
575
579
|
}
|
|
576
580
|
return hash.toString(36);
|
|
577
581
|
}
|
|
582
|
+
mapLoginTypeToAuthMethod(type) {
|
|
583
|
+
switch (type) {
|
|
584
|
+
case "api_key":
|
|
585
|
+
return "api_key";
|
|
586
|
+
case "device_code":
|
|
587
|
+
return "device_code";
|
|
588
|
+
case "oauth":
|
|
589
|
+
case "browser":
|
|
590
|
+
return "oauth_browser";
|
|
591
|
+
default:
|
|
592
|
+
return "unknown";
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
extractDeviceCode(text) {
|
|
596
|
+
const stripped = this.stripAnsiForClassifier(text);
|
|
597
|
+
const explicitMatch = stripped.match(
|
|
598
|
+
/(?:one-time|one time|device)?\s*code[:\s]+([A-Z0-9]{3,}(?:-[A-Z0-9]{3,})+)/i
|
|
599
|
+
);
|
|
600
|
+
if (explicitMatch?.[1]) {
|
|
601
|
+
return explicitMatch[1].toUpperCase();
|
|
602
|
+
}
|
|
603
|
+
if (!/code/i.test(stripped)) {
|
|
604
|
+
return void 0;
|
|
605
|
+
}
|
|
606
|
+
const fallbackMatch = stripped.match(/\b([A-Z0-9]{3,}(?:-[A-Z0-9]{3,})+)\b/);
|
|
607
|
+
return fallbackMatch?.[1]?.toUpperCase();
|
|
608
|
+
}
|
|
609
|
+
getPromptSnippet(maxChars = 280) {
|
|
610
|
+
const normalized = this.stripAnsiForClassifier(this.outputBuffer).replace(/\s+/g, " ").trim();
|
|
611
|
+
if (!normalized) {
|
|
612
|
+
return void 0;
|
|
613
|
+
}
|
|
614
|
+
return normalized.length <= maxChars ? normalized : normalized.slice(-maxChars);
|
|
615
|
+
}
|
|
616
|
+
emitAuthRequired(details) {
|
|
617
|
+
const info = {
|
|
618
|
+
method: this.mapLoginTypeToAuthMethod(details.type),
|
|
619
|
+
url: details.url,
|
|
620
|
+
deviceCode: details.deviceCode ?? this.extractDeviceCode(this.outputBuffer),
|
|
621
|
+
instructions: details.instructions,
|
|
622
|
+
promptSnippet: this.getPromptSnippet()
|
|
623
|
+
};
|
|
624
|
+
this.emit("auth_required", info);
|
|
625
|
+
this.emit("login_required", info.instructions, info.url);
|
|
626
|
+
}
|
|
578
627
|
/**
|
|
579
628
|
* Strip ANSI codes, cursor movement, box-drawing, and spinner characters.
|
|
580
629
|
* Used for stall detection hashing and auto-response pattern matching.
|
|
@@ -934,7 +983,12 @@ var PTYSession = class _PTYSession extends import_events.EventEmitter {
|
|
|
934
983
|
if (loginDetection.required && this._status !== "authenticating") {
|
|
935
984
|
this._status = "authenticating";
|
|
936
985
|
this.clearStallTimer();
|
|
937
|
-
this.
|
|
986
|
+
this.emitAuthRequired({
|
|
987
|
+
type: loginDetection.type,
|
|
988
|
+
url: loginDetection.url,
|
|
989
|
+
deviceCode: loginDetection.deviceCode,
|
|
990
|
+
instructions: loginDetection.instructions
|
|
991
|
+
});
|
|
938
992
|
this.logger.warn(
|
|
939
993
|
{ sessionId: this.id, loginType: loginDetection.type },
|
|
940
994
|
"Login required"
|
|
@@ -977,7 +1031,7 @@ var PTYSession = class _PTYSession extends import_events.EventEmitter {
|
|
|
977
1031
|
instructions: detection.instructions,
|
|
978
1032
|
url: detection.url
|
|
979
1033
|
};
|
|
980
|
-
if (detection.canAutoRespond && detection.suggestedResponse) {
|
|
1034
|
+
if (detection.canAutoRespond && detection.suggestedResponse && !this.config.skipAdapterAutoResponse) {
|
|
981
1035
|
this.logger.info(
|
|
982
1036
|
{
|
|
983
1037
|
sessionId: this.id,
|
|
@@ -1000,7 +1054,13 @@ var PTYSession = class _PTYSession extends import_events.EventEmitter {
|
|
|
1000
1054
|
}
|
|
1001
1055
|
if (detection.type === "login") {
|
|
1002
1056
|
this._status = "authenticating";
|
|
1003
|
-
this.
|
|
1057
|
+
const inferred = this.adapter.detectLogin(this.outputBuffer);
|
|
1058
|
+
this.emitAuthRequired({
|
|
1059
|
+
type: inferred.required ? inferred.type : void 0,
|
|
1060
|
+
url: detection.url ?? inferred.url,
|
|
1061
|
+
deviceCode: inferred.required ? inferred.deviceCode : void 0,
|
|
1062
|
+
instructions: detection.instructions ?? inferred.instructions
|
|
1063
|
+
});
|
|
1004
1064
|
}
|
|
1005
1065
|
this.logger.warn(
|
|
1006
1066
|
{
|
|
@@ -1289,36 +1349,6 @@ var PTYSession = class _PTYSession extends import_events.EventEmitter {
|
|
|
1289
1349
|
};
|
|
1290
1350
|
|
|
1291
1351
|
// src/pty-manager.ts
|
|
1292
|
-
var consoleLogger2 = {
|
|
1293
|
-
debug: (...args) => {
|
|
1294
|
-
if (typeof args[0] === "string") {
|
|
1295
|
-
console.debug(args[0], args[1]);
|
|
1296
|
-
} else {
|
|
1297
|
-
console.debug(args[1], args[0]);
|
|
1298
|
-
}
|
|
1299
|
-
},
|
|
1300
|
-
info: (...args) => {
|
|
1301
|
-
if (typeof args[0] === "string") {
|
|
1302
|
-
console.info(args[0], args[1]);
|
|
1303
|
-
} else {
|
|
1304
|
-
console.info(args[1], args[0]);
|
|
1305
|
-
}
|
|
1306
|
-
},
|
|
1307
|
-
warn: (...args) => {
|
|
1308
|
-
if (typeof args[0] === "string") {
|
|
1309
|
-
console.warn(args[0], args[1]);
|
|
1310
|
-
} else {
|
|
1311
|
-
console.warn(args[1], args[0]);
|
|
1312
|
-
}
|
|
1313
|
-
},
|
|
1314
|
-
error: (...args) => {
|
|
1315
|
-
if (typeof args[0] === "string") {
|
|
1316
|
-
console.error(args[0], args[1]);
|
|
1317
|
-
} else {
|
|
1318
|
-
console.error(args[1], args[0]);
|
|
1319
|
-
}
|
|
1320
|
-
}
|
|
1321
|
-
};
|
|
1322
1352
|
var PTYManager = class extends import_events2.EventEmitter {
|
|
1323
1353
|
sessions = /* @__PURE__ */ new Map();
|
|
1324
1354
|
outputLogs = /* @__PURE__ */ new Map();
|
|
@@ -1332,7 +1362,7 @@ var PTYManager = class extends import_events2.EventEmitter {
|
|
|
1332
1362
|
constructor(config = {}) {
|
|
1333
1363
|
super();
|
|
1334
1364
|
this.adapters = new AdapterRegistry();
|
|
1335
|
-
this.logger = config.logger ||
|
|
1365
|
+
this.logger = config.logger || consoleLogger;
|
|
1336
1366
|
this.maxLogLines = config.maxLogLines || 1e3;
|
|
1337
1367
|
this._stallDetectionEnabled = config.stallDetectionEnabled ?? false;
|
|
1338
1368
|
this._stallTimeoutMs = config.stallTimeoutMs ?? 8e3;
|
|
@@ -1393,6 +1423,9 @@ var PTYManager = class extends import_events2.EventEmitter {
|
|
|
1393
1423
|
session.on("login_required", (instructions, url) => {
|
|
1394
1424
|
this.emit("login_required", session.toHandle(), instructions, url);
|
|
1395
1425
|
});
|
|
1426
|
+
session.on("auth_required", (info) => {
|
|
1427
|
+
this.emit("auth_required", session.toHandle(), info);
|
|
1428
|
+
});
|
|
1396
1429
|
session.on("blocking_prompt", (promptInfo, autoResponded) => {
|
|
1397
1430
|
this.emit("blocking_prompt", session.toHandle(), promptInfo, autoResponded);
|
|
1398
1431
|
});
|
|
@@ -2430,6 +2463,14 @@ var BunCompatiblePTYManager = class extends import_events3.EventEmitter {
|
|
|
2430
2463
|
}
|
|
2431
2464
|
break;
|
|
2432
2465
|
}
|
|
2466
|
+
case "auth_required": {
|
|
2467
|
+
const session = this.sessions.get(id);
|
|
2468
|
+
if (session) {
|
|
2469
|
+
session.status = "authenticating";
|
|
2470
|
+
this.emit("auth_required", session, event.info);
|
|
2471
|
+
}
|
|
2472
|
+
break;
|
|
2473
|
+
}
|
|
2433
2474
|
case "message": {
|
|
2434
2475
|
const msg = event.message;
|
|
2435
2476
|
this.emit("message", {
|