echo-cli-agent 0.1.8 → 0.1.10
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/src/dispatch/dispatcher.d.ts +13 -2
- package/dist/src/dispatch/dispatcher.js +72 -1
- package/dist/src/dispatch/dispatcher.js.map +1 -1
- package/dist/src/health/server.d.ts +6 -0
- package/dist/src/health/server.js.map +1 -1
- package/dist/src/runtime/agent-runtime.js +15 -10
- package/dist/src/runtime/agent-runtime.js.map +1 -1
- package/dist/src/session/session-manager.d.ts +37 -0
- package/dist/src/session/session-manager.js +84 -18
- package/dist/src/session/session-manager.js.map +1 -1
- package/dist/src/session/terminal-line-sanitizer.d.ts +7 -0
- package/dist/src/session/terminal-line-sanitizer.js +21 -0
- package/dist/src/session/terminal-line-sanitizer.js.map +1 -0
- package/dist/src/session/terminal-tail-buffer.d.ts +70 -0
- package/dist/src/session/terminal-tail-buffer.js +149 -0
- package/dist/src/session/terminal-tail-buffer.js.map +1 -0
- package/dist/src/transport/taskmcp-client.d.ts +11 -1
- package/dist/src/transport/taskmcp-client.js +41 -0
- package/dist/src/transport/taskmcp-client.js.map +1 -1
- package/dist/src/types/protocol.d.ts +39 -0
- package/dist/src/types/protocol.js +27 -0
- package/dist/src/types/protocol.js.map +1 -1
- package/dist/tests/terminal-tail-buffer.test.d.ts +1 -0
- package/dist/tests/terminal-tail-buffer.test.js +68 -0
- package/dist/tests/terminal-tail-buffer.test.js.map +1 -0
- package/package.json +1 -1
- package/scripts/service.mjs +25 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal row sanitization. The escape, control-byte and credential patterns
|
|
3
|
+
* live here so there is a single place to audit what can leave the device when
|
|
4
|
+
* terminal text is read on demand.
|
|
5
|
+
*/
|
|
6
|
+
// CSI (`ESC [ … final`), terminated OSC (`ESC ] … BEL/ST`) and two-character escapes.
|
|
7
|
+
const TERMINAL_ESCAPE = /\u001b(?:\][^\u0007]*?(?:\u0007|\u001b\\)|\[[0-?]*[ -/]*[@-~]|[@-Z\\-_])/g;
|
|
8
|
+
// Everything unprintable except tab.
|
|
9
|
+
const CONTROL_CHARACTER = /[\u0000-\u0008\u000b-\u001f\u007f]/g;
|
|
10
|
+
// A labeled key redacts to end of line: `Authorization: Bearer <token>` would
|
|
11
|
+
// otherwise stop at the scheme and leave the token itself in the output.
|
|
12
|
+
const SENSITIVE_VALUE = /\b(?:sk|rk|pk|ghp|github_pat)_[A-Za-z0-9._-]+|\bBearer\s+\S+|\b(?:api[_-]?key|token|authorization|x-mcp-token)\s*[:=]\s*\S[^\r\n]*/gi;
|
|
13
|
+
/** Strip escapes and control bytes, redact credential-shaped values, drop cell padding. */
|
|
14
|
+
export function sanitizeTerminalLine(line) {
|
|
15
|
+
return line
|
|
16
|
+
.replace(TERMINAL_ESCAPE, '')
|
|
17
|
+
.replace(CONTROL_CHARACTER, '')
|
|
18
|
+
.replace(SENSITIVE_VALUE, '[redacted]')
|
|
19
|
+
.replace(/\s+$/, '');
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=terminal-line-sanitizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal-line-sanitizer.js","sourceRoot":"","sources":["../../../src/session/terminal-line-sanitizer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,sFAAsF;AACtF,MAAM,eAAe,GAAG,2EAA2E,CAAA;AACnG,qCAAqC;AACrC,MAAM,iBAAiB,GAAG,qCAAqC,CAAA;AAC/D,8EAA8E;AAC9E,yEAAyE;AACzE,MAAM,eAAe,GACnB,sIAAsI,CAAA;AAExI,2FAA2F;AAC3F,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,OAAO,IAAI;SACR,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC;SAC5B,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC;SAC9B,OAAO,CAAC,eAAe,EAAE,YAAY,CAAC;SACtC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;AACxB,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bounded terminal tail with absolute line cursors, so a client can read the
|
|
3
|
+
* newest rows and then page backwards without re-reading what it already has.
|
|
4
|
+
*
|
|
5
|
+
* Mirrors Orca's split between a line-oriented tail and the still-unterminated
|
|
6
|
+
* partial line. Two properties matter for cost:
|
|
7
|
+
*
|
|
8
|
+
* - Appends are O(chunk). The naive `output = (output + chunk).slice(-N)` form
|
|
9
|
+
* re-materializes the whole retained tail on every PTY chunk, which is O(N)
|
|
10
|
+
* per write; a busy CLI turns that into a per-keystroke copy of 128 KiB.
|
|
11
|
+
* - Reads are O(requested lines). Escape stripping and redaction run on the
|
|
12
|
+
* returned rows only, never on the whole buffer.
|
|
13
|
+
*
|
|
14
|
+
* Retained rows are raw. Sanitizing on read keeps escape sequences that straddle
|
|
15
|
+
* a chunk boundary intact until the full sequence has arrived.
|
|
16
|
+
*/
|
|
17
|
+
/** Retained completed rows. Matches Orca's `MAX_TAIL_LINES`. */
|
|
18
|
+
export declare const MAX_TAIL_LINES = 2000;
|
|
19
|
+
/** Retained characters across completed rows. Matches Orca's `MAX_TAIL_CHARS`. */
|
|
20
|
+
export declare const MAX_TAIL_CHARS: number;
|
|
21
|
+
/** A CLI that redraws a spinner without ever emitting a newline cannot grow past this. */
|
|
22
|
+
export declare const MAX_TAIL_PARTIAL_CHARS = 4000;
|
|
23
|
+
/** Rows returned when the caller does not ask for a specific count. */
|
|
24
|
+
export declare const DEFAULT_TAIL_READ_LIMIT = 120;
|
|
25
|
+
export declare const MAX_TAIL_READ_LIMIT = 2000;
|
|
26
|
+
/** Character budget applied after the row limit, so wide rows cannot blow up a response. */
|
|
27
|
+
export declare const MAX_TAIL_READ_CHARS: number;
|
|
28
|
+
export type TerminalTailReadOptions = {
|
|
29
|
+
/** Rows to return, clamped to `MAX_TAIL_READ_LIMIT`. */
|
|
30
|
+
limit?: number;
|
|
31
|
+
/** Absolute cursor to read backwards from. Omit for the newest rows. */
|
|
32
|
+
beforeCursor?: number;
|
|
33
|
+
};
|
|
34
|
+
export type TerminalTailRead = {
|
|
35
|
+
lines: string[];
|
|
36
|
+
/** Absolute index of `lines[0]`. Pass it back as `beforeCursor` to page further up. */
|
|
37
|
+
startCursor: number;
|
|
38
|
+
/** Absolute index just past the last completed row returned. */
|
|
39
|
+
nextCursor: number;
|
|
40
|
+
/** Oldest absolute index still retained. `startCursor` cannot go below it. */
|
|
41
|
+
oldestCursor: number;
|
|
42
|
+
/** Absolute count of rows completed so far. */
|
|
43
|
+
latestCursor: number;
|
|
44
|
+
/** More retained rows exist above `startCursor`. */
|
|
45
|
+
hasMore: boolean;
|
|
46
|
+
/** Rows older than `oldestCursor` were dropped to stay within the caps. */
|
|
47
|
+
truncated: boolean;
|
|
48
|
+
/** The character budget clipped the response. */
|
|
49
|
+
limited: boolean;
|
|
50
|
+
/** Milliseconds since the CLI last wrote anything, or undefined if it never did. */
|
|
51
|
+
idleMs: number | undefined;
|
|
52
|
+
};
|
|
53
|
+
export declare class TerminalTailBuffer {
|
|
54
|
+
/** Completed rows, raw. Dropped head slots are blanked, then compacted in batches. */
|
|
55
|
+
private lines;
|
|
56
|
+
private head;
|
|
57
|
+
private chars;
|
|
58
|
+
/** Absolute count of completed rows, including dropped ones. Defines cursor space. */
|
|
59
|
+
private completedCount;
|
|
60
|
+
private partial;
|
|
61
|
+
private droppedRows;
|
|
62
|
+
private lastOutputAt;
|
|
63
|
+
get lastWriteAt(): number | undefined;
|
|
64
|
+
append(chunk: string, at?: number): void;
|
|
65
|
+
read(options?: TerminalTailReadOptions, now?: number): TerminalTailRead;
|
|
66
|
+
private appendPartial;
|
|
67
|
+
private commitPartial;
|
|
68
|
+
private trim;
|
|
69
|
+
private dropHead;
|
|
70
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { sanitizeTerminalLine } from './terminal-line-sanitizer.js';
|
|
2
|
+
/**
|
|
3
|
+
* Bounded terminal tail with absolute line cursors, so a client can read the
|
|
4
|
+
* newest rows and then page backwards without re-reading what it already has.
|
|
5
|
+
*
|
|
6
|
+
* Mirrors Orca's split between a line-oriented tail and the still-unterminated
|
|
7
|
+
* partial line. Two properties matter for cost:
|
|
8
|
+
*
|
|
9
|
+
* - Appends are O(chunk). The naive `output = (output + chunk).slice(-N)` form
|
|
10
|
+
* re-materializes the whole retained tail on every PTY chunk, which is O(N)
|
|
11
|
+
* per write; a busy CLI turns that into a per-keystroke copy of 128 KiB.
|
|
12
|
+
* - Reads are O(requested lines). Escape stripping and redaction run on the
|
|
13
|
+
* returned rows only, never on the whole buffer.
|
|
14
|
+
*
|
|
15
|
+
* Retained rows are raw. Sanitizing on read keeps escape sequences that straddle
|
|
16
|
+
* a chunk boundary intact until the full sequence has arrived.
|
|
17
|
+
*/
|
|
18
|
+
/** Retained completed rows. Matches Orca's `MAX_TAIL_LINES`. */
|
|
19
|
+
export const MAX_TAIL_LINES = 2_000;
|
|
20
|
+
/** Retained characters across completed rows. Matches Orca's `MAX_TAIL_CHARS`. */
|
|
21
|
+
export const MAX_TAIL_CHARS = 256 * 1024;
|
|
22
|
+
/** A CLI that redraws a spinner without ever emitting a newline cannot grow past this. */
|
|
23
|
+
export const MAX_TAIL_PARTIAL_CHARS = 4_000;
|
|
24
|
+
/** Rows returned when the caller does not ask for a specific count. */
|
|
25
|
+
export const DEFAULT_TAIL_READ_LIMIT = 120;
|
|
26
|
+
export const MAX_TAIL_READ_LIMIT = MAX_TAIL_LINES;
|
|
27
|
+
/** Character budget applied after the row limit, so wide rows cannot blow up a response. */
|
|
28
|
+
export const MAX_TAIL_READ_CHARS = 32 * 1024;
|
|
29
|
+
/** Empty head slots tolerated before the backing array is compacted. */
|
|
30
|
+
const HEAD_COMPACT_THRESHOLD = 1_024;
|
|
31
|
+
export class TerminalTailBuffer {
|
|
32
|
+
/** Completed rows, raw. Dropped head slots are blanked, then compacted in batches. */
|
|
33
|
+
lines = [];
|
|
34
|
+
head = 0;
|
|
35
|
+
chars = 0;
|
|
36
|
+
/** Absolute count of completed rows, including dropped ones. Defines cursor space. */
|
|
37
|
+
completedCount = 0;
|
|
38
|
+
partial = '';
|
|
39
|
+
droppedRows = false;
|
|
40
|
+
lastOutputAt;
|
|
41
|
+
get lastWriteAt() {
|
|
42
|
+
return this.lastOutputAt;
|
|
43
|
+
}
|
|
44
|
+
append(chunk, at = Date.now()) {
|
|
45
|
+
if (!chunk)
|
|
46
|
+
return;
|
|
47
|
+
this.lastOutputAt = at;
|
|
48
|
+
let index = 0;
|
|
49
|
+
for (;;) {
|
|
50
|
+
const newline = chunk.indexOf('\n', index);
|
|
51
|
+
if (newline === -1) {
|
|
52
|
+
this.appendPartial(chunk.slice(index));
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
// Trim the CR of a CRLF pair so it does not survive as a row rewrite.
|
|
56
|
+
const end = newline > index && chunk.charCodeAt(newline - 1) === 13 ? newline - 1 : newline;
|
|
57
|
+
this.appendPartial(chunk.slice(index, end));
|
|
58
|
+
this.commitPartial();
|
|
59
|
+
index = newline + 1;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
read(options = {}, now = Date.now()) {
|
|
63
|
+
const limit = clampLimit(options.limit);
|
|
64
|
+
const retained = this.lines.length - this.head;
|
|
65
|
+
const oldestCursor = this.completedCount - retained;
|
|
66
|
+
const latestCursor = this.completedCount;
|
|
67
|
+
// A preview read shows what the screen shows, including the row being written.
|
|
68
|
+
const includePartial = options.beforeCursor === undefined && this.partial.length > 0;
|
|
69
|
+
const endCursor = options.beforeCursor === undefined
|
|
70
|
+
? latestCursor
|
|
71
|
+
: Math.min(Math.max(options.beforeCursor, oldestCursor), latestCursor);
|
|
72
|
+
const rowBudget = Math.max(0, limit - (includePartial ? 1 : 0));
|
|
73
|
+
const startCursor = Math.max(oldestCursor, endCursor - rowBudget);
|
|
74
|
+
const rows = [];
|
|
75
|
+
for (let cursor = startCursor; cursor < endCursor; cursor += 1) {
|
|
76
|
+
rows.push(sanitizeTerminalLine(this.lines[this.head + (cursor - oldestCursor)] ?? ''));
|
|
77
|
+
}
|
|
78
|
+
if (includePartial)
|
|
79
|
+
rows.push(sanitizeTerminalLine(this.partial));
|
|
80
|
+
const clipped = clipToCharacterBudget(rows, MAX_TAIL_READ_CHARS);
|
|
81
|
+
return {
|
|
82
|
+
lines: clipped.lines,
|
|
83
|
+
startCursor,
|
|
84
|
+
nextCursor: endCursor,
|
|
85
|
+
oldestCursor,
|
|
86
|
+
latestCursor,
|
|
87
|
+
hasMore: startCursor > oldestCursor,
|
|
88
|
+
truncated: this.droppedRows,
|
|
89
|
+
limited: clipped.limited,
|
|
90
|
+
idleMs: this.lastOutputAt === undefined ? undefined : Math.max(0, now - this.lastOutputAt)
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
appendPartial(text) {
|
|
94
|
+
if (!text)
|
|
95
|
+
return;
|
|
96
|
+
const carriage = text.lastIndexOf('\r');
|
|
97
|
+
// A bare CR rewrites the current row on a real terminal; keep only what would stay visible.
|
|
98
|
+
const next = carriage === -1 ? `${this.partial}${text}` : text.slice(carriage + 1);
|
|
99
|
+
this.partial =
|
|
100
|
+
next.length > MAX_TAIL_PARTIAL_CHARS ? next.slice(-MAX_TAIL_PARTIAL_CHARS) : next;
|
|
101
|
+
}
|
|
102
|
+
commitPartial() {
|
|
103
|
+
const line = this.partial;
|
|
104
|
+
this.partial = '';
|
|
105
|
+
this.lines.push(line);
|
|
106
|
+
this.chars += line.length;
|
|
107
|
+
this.completedCount += 1;
|
|
108
|
+
this.trim();
|
|
109
|
+
}
|
|
110
|
+
trim() {
|
|
111
|
+
while (this.lines.length - this.head > MAX_TAIL_LINES)
|
|
112
|
+
this.dropHead();
|
|
113
|
+
while (this.head < this.lines.length && this.chars > MAX_TAIL_CHARS)
|
|
114
|
+
this.dropHead();
|
|
115
|
+
if (this.head >= HEAD_COMPACT_THRESHOLD) {
|
|
116
|
+
this.lines = this.lines.slice(this.head);
|
|
117
|
+
this.head = 0;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
dropHead() {
|
|
121
|
+
const line = this.lines[this.head];
|
|
122
|
+
if (line === undefined)
|
|
123
|
+
return;
|
|
124
|
+
this.chars -= line.length;
|
|
125
|
+
// Release the row before compaction so a large tail is not pinned by an empty slot.
|
|
126
|
+
this.lines[this.head] = '';
|
|
127
|
+
this.head += 1;
|
|
128
|
+
this.droppedRows = true;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function clampLimit(limit) {
|
|
132
|
+
if (limit === undefined || !Number.isFinite(limit))
|
|
133
|
+
return DEFAULT_TAIL_READ_LIMIT;
|
|
134
|
+
return Math.max(1, Math.min(Math.floor(limit), MAX_TAIL_READ_LIMIT));
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Apply the character budget from the newest row backwards, so the rows closest
|
|
138
|
+
* to what the CLI is doing now are the ones that survive.
|
|
139
|
+
*/
|
|
140
|
+
function clipToCharacterBudget(rows, budget) {
|
|
141
|
+
let total = 0;
|
|
142
|
+
for (let index = rows.length - 1; index >= 0; index -= 1) {
|
|
143
|
+
total += rows[index].length + 1;
|
|
144
|
+
if (total > budget)
|
|
145
|
+
return { lines: rows.slice(index + 1), limited: true };
|
|
146
|
+
}
|
|
147
|
+
return { lines: rows, limited: false };
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=terminal-tail-buffer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal-tail-buffer.js","sourceRoot":"","sources":["../../../src/session/terminal-tail-buffer.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import { EventEmitter } from 'node:events';
|
|
2
2
|
import type { AgentConfig } from '../config.js';
|
|
3
3
|
import type { DiagnosticLogger } from '../logging/diagnostic-log.js';
|
|
4
|
-
import type { AgentCliAck, AgentCliControlAck, AgentCliEvent, ExecScriptAck, LocalCliDetectAck, WorkspaceAck } from '../types/protocol.js';
|
|
4
|
+
import type { AgentCliAck, AgentCliControlAck, AgentCliEvent, AgentCliOutputTailAck, ExecScriptAck, LocalCliDetectAck, WorkspaceAck } from '../types/protocol.js';
|
|
5
5
|
type ClientEvents = {
|
|
6
6
|
message: [unknown];
|
|
7
7
|
connection: [boolean];
|
|
8
8
|
};
|
|
9
|
+
export type ConnectionFailure = {
|
|
10
|
+
/** HTTP status of a rejected upgrade, absent for transport-level failures. */
|
|
11
|
+
status?: number;
|
|
12
|
+
reason: string;
|
|
13
|
+
at: string;
|
|
14
|
+
};
|
|
9
15
|
export declare class TaskMcpClient extends EventEmitter<ClientEvents> {
|
|
10
16
|
private readonly config;
|
|
11
17
|
private readonly deviceId;
|
|
@@ -15,8 +21,11 @@ export declare class TaskMcpClient extends EventEmitter<ClientEvents> {
|
|
|
15
21
|
private retryAttempt;
|
|
16
22
|
private reconnectTimer;
|
|
17
23
|
private heartbeat;
|
|
24
|
+
private failure;
|
|
18
25
|
constructor(config: AgentConfig, deviceId: string, logger: DiagnosticLogger);
|
|
19
26
|
get connected(): boolean;
|
|
27
|
+
/** Last reason the connection failed, cleared once a handshake succeeds. */
|
|
28
|
+
get lastFailure(): ConnectionFailure | undefined;
|
|
20
29
|
start(): void;
|
|
21
30
|
stop(): void;
|
|
22
31
|
sendEvent(event: AgentCliEvent): Promise<void>;
|
|
@@ -25,6 +34,7 @@ export declare class TaskMcpClient extends EventEmitter<ClientEvents> {
|
|
|
25
34
|
sendExecScriptAck(ack: ExecScriptAck): Promise<void>;
|
|
26
35
|
sendWorkspaceAck(ack: WorkspaceAck): Promise<void>;
|
|
27
36
|
sendLocalCliDetectAck(ack: LocalCliDetectAck): Promise<void>;
|
|
37
|
+
sendOutputTailAck(ack: AgentCliOutputTailAck): Promise<void>;
|
|
28
38
|
private send;
|
|
29
39
|
private connect;
|
|
30
40
|
private scheduleReconnect;
|
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
import { EventEmitter } from 'node:events';
|
|
2
2
|
import WebSocket from 'ws';
|
|
3
|
+
/**
|
|
4
|
+
* Why the handshake was refused, in terms the operator can act on. A rejected
|
|
5
|
+
* upgrade retries forever otherwise, and `connected: false` alone does not say
|
|
6
|
+
* whether the credential, the URL, or the network is at fault.
|
|
7
|
+
*/
|
|
8
|
+
function handshakeReason(status) {
|
|
9
|
+
if (status === 401 || status === 403) {
|
|
10
|
+
return `服务端拒绝了设备凭据(${status})。凭据可能已被轮换或吊销,请在客户端重新配对该设备。`;
|
|
11
|
+
}
|
|
12
|
+
if (status === 429)
|
|
13
|
+
return '认证失败次数过多,已被限流(429)。请稍后重试。';
|
|
14
|
+
if (status === 404)
|
|
15
|
+
return 'WebSocket 地址不存在(404)。请检查 wsUrl 是否正确。';
|
|
16
|
+
if (status >= 500)
|
|
17
|
+
return `服务端暂时不可用(${status})。`;
|
|
18
|
+
return `服务端拒绝了 WebSocket 握手(${status})。`;
|
|
19
|
+
}
|
|
3
20
|
export class TaskMcpClient extends EventEmitter {
|
|
4
21
|
config;
|
|
5
22
|
deviceId;
|
|
@@ -9,6 +26,7 @@ export class TaskMcpClient extends EventEmitter {
|
|
|
9
26
|
retryAttempt = 0;
|
|
10
27
|
reconnectTimer;
|
|
11
28
|
heartbeat;
|
|
29
|
+
failure;
|
|
12
30
|
constructor(config, deviceId, logger) {
|
|
13
31
|
super();
|
|
14
32
|
this.config = config;
|
|
@@ -18,6 +36,10 @@ export class TaskMcpClient extends EventEmitter {
|
|
|
18
36
|
get connected() {
|
|
19
37
|
return this.socket?.readyState === WebSocket.OPEN;
|
|
20
38
|
}
|
|
39
|
+
/** Last reason the connection failed, cleared once a handshake succeeds. */
|
|
40
|
+
get lastFailure() {
|
|
41
|
+
return this.failure;
|
|
42
|
+
}
|
|
21
43
|
start() {
|
|
22
44
|
this.stopped = false;
|
|
23
45
|
this.connect();
|
|
@@ -47,6 +69,9 @@ export class TaskMcpClient extends EventEmitter {
|
|
|
47
69
|
return this.send(ack);
|
|
48
70
|
}
|
|
49
71
|
sendLocalCliDetectAck(ack) { return this.send(ack); }
|
|
72
|
+
sendOutputTailAck(ack) {
|
|
73
|
+
return this.send(ack);
|
|
74
|
+
}
|
|
50
75
|
send(message) {
|
|
51
76
|
return new Promise((resolveSend, rejectSend) => {
|
|
52
77
|
if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
|
|
@@ -76,8 +101,23 @@ export class TaskMcpClient extends EventEmitter {
|
|
|
76
101
|
const headers = this.config.authMode === 'mcp' ? { 'X-MCP-Token': this.config.authToken } : undefined;
|
|
77
102
|
const socket = new WebSocket(url, { ...(headers ? { headers } : {}) });
|
|
78
103
|
this.socket = socket;
|
|
104
|
+
// ws suppresses its default error for a rejected upgrade once this is
|
|
105
|
+
// observed, so the reconnect has to be scheduled here.
|
|
106
|
+
socket.on('unexpected-response', (request, response) => {
|
|
107
|
+
const status = response.statusCode ?? 0;
|
|
108
|
+
const reason = handshakeReason(status);
|
|
109
|
+
this.failure = { status, reason, at: new Date().toISOString() };
|
|
110
|
+
this.logger.error('taskmcp-handshake-rejected', { status, reason });
|
|
111
|
+
response.resume();
|
|
112
|
+
request.destroy();
|
|
113
|
+
if (this.socket === socket)
|
|
114
|
+
this.socket = undefined;
|
|
115
|
+
this.emit('connection', false);
|
|
116
|
+
this.scheduleReconnect();
|
|
117
|
+
});
|
|
79
118
|
socket.on('open', () => {
|
|
80
119
|
this.retryAttempt = 0;
|
|
120
|
+
this.failure = undefined;
|
|
81
121
|
this.logger.info('taskmcp-connected', { deviceId: this.deviceId });
|
|
82
122
|
this.emit('connection', true);
|
|
83
123
|
this.heartbeat = setInterval(() => {
|
|
@@ -95,6 +135,7 @@ export class TaskMcpClient extends EventEmitter {
|
|
|
95
135
|
}
|
|
96
136
|
});
|
|
97
137
|
socket.on('error', (error) => {
|
|
138
|
+
this.failure = { reason: error.message, at: new Date().toISOString() };
|
|
98
139
|
this.logger.warn('taskmcp-socket-error', { error: error.message });
|
|
99
140
|
});
|
|
100
141
|
socket.on('close', (code) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"taskmcp-client.js","sourceRoot":"","sources":["../../../src/transport/taskmcp-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,SAAS,MAAM,IAAI,CAAA;
|
|
1
|
+
{"version":3,"file":"taskmcp-client.js","sourceRoot":"","sources":["../../../src/transport/taskmcp-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -47,6 +47,45 @@ export type LocalCliDetectAck = {
|
|
|
47
47
|
snapshot?: Record<string, unknown>;
|
|
48
48
|
error?: string;
|
|
49
49
|
};
|
|
50
|
+
/**
|
|
51
|
+
* On-demand read of a live CLI session's newest terminal rows. Modelled on
|
|
52
|
+
* Orca's `terminal.read`: no cursor returns a preview of the newest rows, a
|
|
53
|
+
* cursor pages backwards through rows the reader has not seen yet.
|
|
54
|
+
*
|
|
55
|
+
* Either `run_id` or `task_id` must identify the session. Sessions are reused
|
|
56
|
+
* across runs, so `task_id` still resolves after a later prompt took over.
|
|
57
|
+
*/
|
|
58
|
+
export declare const AgentCliOutputTailRequestSchema: z.ZodObject<{
|
|
59
|
+
type: z.ZodLiteral<"agent_cli.output_tail">;
|
|
60
|
+
request_id: z.ZodString;
|
|
61
|
+
run_id: z.ZodOptional<z.ZodString>;
|
|
62
|
+
task_id: z.ZodOptional<z.ZodString>;
|
|
63
|
+
agent_type: z.ZodOptional<z.ZodString>;
|
|
64
|
+
lines: z.ZodOptional<z.ZodNumber>;
|
|
65
|
+
before_cursor: z.ZodOptional<z.ZodNumber>;
|
|
66
|
+
}, z.core.$strip>;
|
|
67
|
+
export type AgentCliOutputTailRequest = z.infer<typeof AgentCliOutputTailRequestSchema>;
|
|
68
|
+
export type AgentCliOutputTailAck = {
|
|
69
|
+
type: 'agent_cli.output_tail.ack';
|
|
70
|
+
request_id: string;
|
|
71
|
+
status: 'done' | 'idle' | 'failed';
|
|
72
|
+
run_id?: string;
|
|
73
|
+
session_id?: string;
|
|
74
|
+
agent_type?: string;
|
|
75
|
+
lines?: string[];
|
|
76
|
+
/** Absolute index of the first returned row; pass back as `before_cursor` to page up. */
|
|
77
|
+
start_cursor?: number;
|
|
78
|
+
next_cursor?: number;
|
|
79
|
+
oldest_cursor?: number;
|
|
80
|
+
latest_cursor?: number;
|
|
81
|
+
has_more?: boolean;
|
|
82
|
+
truncated?: boolean;
|
|
83
|
+
limited?: boolean;
|
|
84
|
+
/** Milliseconds since the CLI last wrote. The strongest "is it alive" signal. */
|
|
85
|
+
idle_ms?: number | null;
|
|
86
|
+
markers?: string[];
|
|
87
|
+
error?: string;
|
|
88
|
+
};
|
|
50
89
|
export declare const ExecScriptRequestSchema: z.ZodObject<{
|
|
51
90
|
type: z.ZodLiteral<"exec_script">;
|
|
52
91
|
log_id: z.ZodString;
|
|
@@ -40,6 +40,33 @@ export const LocalCliDetectRequestSchema = z.object({
|
|
|
40
40
|
request_id: z.string().min(1).max(128),
|
|
41
41
|
refresh: z.boolean().optional().default(false)
|
|
42
42
|
});
|
|
43
|
+
/**
|
|
44
|
+
* On-demand read of a live CLI session's newest terminal rows. Modelled on
|
|
45
|
+
* Orca's `terminal.read`: no cursor returns a preview of the newest rows, a
|
|
46
|
+
* cursor pages backwards through rows the reader has not seen yet.
|
|
47
|
+
*
|
|
48
|
+
* Either `run_id` or `task_id` must identify the session. Sessions are reused
|
|
49
|
+
* across runs, so `task_id` still resolves after a later prompt took over.
|
|
50
|
+
*/
|
|
51
|
+
export const AgentCliOutputTailRequestSchema = z
|
|
52
|
+
.object({
|
|
53
|
+
type: z.literal('agent_cli.output_tail'),
|
|
54
|
+
request_id: z.string().min(1).max(128),
|
|
55
|
+
run_id: z.string().min(1).max(128).optional(),
|
|
56
|
+
task_id: z.string().min(1).max(128).optional(),
|
|
57
|
+
agent_type: z.string().min(1).max(40).optional(),
|
|
58
|
+
lines: z.number().int().positive().max(2_000).optional(),
|
|
59
|
+
before_cursor: z.number().int().min(0).optional()
|
|
60
|
+
})
|
|
61
|
+
.superRefine((request, context) => {
|
|
62
|
+
if (!request.run_id?.trim() && !request.task_id?.trim()) {
|
|
63
|
+
context.addIssue({
|
|
64
|
+
code: 'custom',
|
|
65
|
+
path: ['run_id'],
|
|
66
|
+
message: 'output tail requests must provide run_id or task_id'
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
});
|
|
43
70
|
export const ExecScriptRequestSchema = z.object({
|
|
44
71
|
type: z.literal('exec_script'),
|
|
45
72
|
log_id: z.string().min(1).max(128),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../../src/types/protocol.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../../src/types/protocol.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { MAX_TAIL_LINES, TerminalTailBuffer } from '../src/session/terminal-tail-buffer.js';
|
|
3
|
+
describe('TerminalTailBuffer', () => {
|
|
4
|
+
it('returns the newest rows and pages backwards without gaps or repeats', () => {
|
|
5
|
+
const buffer = new TerminalTailBuffer();
|
|
6
|
+
for (let index = 1; index <= 10; index += 1)
|
|
7
|
+
buffer.append(`row ${index}\n`);
|
|
8
|
+
const latest = buffer.read({ limit: 4 });
|
|
9
|
+
expect(latest.lines).toEqual(['row 7', 'row 8', 'row 9', 'row 10']);
|
|
10
|
+
expect(latest.startCursor).toBe(6);
|
|
11
|
+
expect(latest.latestCursor).toBe(10);
|
|
12
|
+
expect(latest.hasMore).toBe(true);
|
|
13
|
+
const older = buffer.read({ limit: 4, beforeCursor: latest.startCursor });
|
|
14
|
+
expect(older.lines).toEqual(['row 3', 'row 4', 'row 5', 'row 6']);
|
|
15
|
+
expect(older.nextCursor).toBe(latest.startCursor);
|
|
16
|
+
const oldest = buffer.read({ limit: 4, beforeCursor: older.startCursor });
|
|
17
|
+
expect(oldest.lines).toEqual(['row 1', 'row 2']);
|
|
18
|
+
expect(oldest.hasMore).toBe(false);
|
|
19
|
+
});
|
|
20
|
+
it('shows the row still being written, and excludes it from cursor reads', () => {
|
|
21
|
+
const buffer = new TerminalTailBuffer();
|
|
22
|
+
buffer.append('done\nin progre');
|
|
23
|
+
const latest = buffer.read();
|
|
24
|
+
expect(latest.lines).toEqual(['done', 'in progre']);
|
|
25
|
+
expect(latest.latestCursor).toBe(1);
|
|
26
|
+
buffer.append('ss\n');
|
|
27
|
+
expect(buffer.read().lines).toEqual(['done', 'in progress']);
|
|
28
|
+
});
|
|
29
|
+
it('keeps only the visible text of a carriage-return redraw', () => {
|
|
30
|
+
const buffer = new TerminalTailBuffer();
|
|
31
|
+
buffer.append('Thinking 10%\rThinking 55%\rThinking 100%\n');
|
|
32
|
+
expect(buffer.read().lines).toEqual(['Thinking 100%']);
|
|
33
|
+
});
|
|
34
|
+
it('splits CRLF without leaving a stray carriage return', () => {
|
|
35
|
+
const buffer = new TerminalTailBuffer();
|
|
36
|
+
buffer.append('first\r\nsecond\r\n');
|
|
37
|
+
expect(buffer.read().lines).toEqual(['first', 'second']);
|
|
38
|
+
});
|
|
39
|
+
it('strips escape sequences and redacts credential-shaped values', () => {
|
|
40
|
+
const buffer = new TerminalTailBuffer();
|
|
41
|
+
buffer.append('\u001b[32mok\u001b[0m \nAuthorization: Bearer abc.def\n');
|
|
42
|
+
buffer.append('loaded sk_live_abcdefgh into env\n');
|
|
43
|
+
expect(buffer.read().lines).toEqual([
|
|
44
|
+
'ok',
|
|
45
|
+
'[redacted]',
|
|
46
|
+
'loaded [redacted] into env'
|
|
47
|
+
]);
|
|
48
|
+
});
|
|
49
|
+
it('drops the oldest rows past the retention cap and reports truncation', () => {
|
|
50
|
+
const buffer = new TerminalTailBuffer();
|
|
51
|
+
for (let index = 0; index < MAX_TAIL_LINES + 50; index += 1) {
|
|
52
|
+
buffer.append(`row ${index}\n`);
|
|
53
|
+
}
|
|
54
|
+
const read = buffer.read({ limit: 1 });
|
|
55
|
+
expect(read.truncated).toBe(true);
|
|
56
|
+
expect(read.oldestCursor).toBe(50);
|
|
57
|
+
expect(read.latestCursor).toBe(MAX_TAIL_LINES + 50);
|
|
58
|
+
// A cursor below the retained range clamps instead of returning stale rows.
|
|
59
|
+
expect(buffer.read({ limit: 5, beforeCursor: 10 }).lines).toEqual([]);
|
|
60
|
+
});
|
|
61
|
+
it('reports how long the CLI has been silent', () => {
|
|
62
|
+
const buffer = new TerminalTailBuffer();
|
|
63
|
+
expect(buffer.read().idleMs).toBeUndefined();
|
|
64
|
+
buffer.append('working\n', 1_000);
|
|
65
|
+
expect(buffer.read({}, 4_500).idleMs).toBe(3_500);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
//# sourceMappingURL=terminal-tail-buffer.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal-tail-buffer.test.js","sourceRoot":"","sources":["../../tests/terminal-tail-buffer.test.ts"],"names":[],"mappings":"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"}
|