@syrkos/filaxis 0.1.0-alpha.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/AGENTS.md +46 -0
- package/LICENSE +202 -0
- package/NOTICE +10 -0
- package/README.md +61 -0
- package/bin/filaxis.ts +112 -0
- package/dist/agent-toolkit.d.ts +99 -0
- package/dist/dialect.d.ts +31 -0
- package/dist/driver.d.ts +130 -0
- package/dist/events.d.ts +26 -0
- package/dist/factory.d.ts +16 -0
- package/dist/file-edit.d.ts +43 -0
- package/dist/file-transfer.d.ts +44 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.js +4200 -0
- package/dist/layer-discovery.d.ts +30 -0
- package/dist/layer-env.d.ts +95 -0
- package/dist/local-driver.d.ts +47 -0
- package/dist/local-session-manager.d.ts +98 -0
- package/dist/logger.d.ts +12 -0
- package/dist/remote-session-manager.d.ts +64 -0
- package/dist/runtime-stack.d.ts +52 -0
- package/dist/session-buffer.d.ts +73 -0
- package/dist/session-types.d.ts +74 -0
- package/dist/shell-discovery.d.ts +4 -0
- package/dist/ssh-helpers.d.ts +88 -0
- package/dist/ssh-pty-driver.d.ts +74 -0
- package/dist/stream-session-manager.d.ts +83 -0
- package/dist/stream-window.d.ts +124 -0
- package/dist/terminal/command-executer.d.ts +73 -0
- package/dist/terminal/output-buffer.d.ts +49 -0
- package/dist/terminal/session-launcher.d.ts +28 -0
- package/dist/terminal/session-registry.d.ts +29 -0
- package/dist/terminal/tmux-invoker.d.ts +8 -0
- package/dist/terminal/types.d.ts +56 -0
- package/dist/terminal-manager.d.ts +30 -0
- package/dist/tmux-driver.d.ts +49 -0
- package/dist/vt100-screen.d.ts +46 -0
- package/docs/DEVELOPMENT.md +38 -0
- package/docs/KNOWN-ISSUES.md +35 -0
- package/docs/implementation-notes.md +57 -0
- package/docs/skills/cross-host-diagnosis.md +21 -0
- package/docs/skills/file-sync.md +22 -0
- package/docs/skills/longrun-observe.md +20 -0
- package/docs/skills/stream-window.md +28 -0
- package/docs//345/256/214/346/225/264/345/267/241/346/243/200/346/212/245/345/221/212.md +141 -0
- package/docs//346/265/213/350/257/2252.md +112 -0
- package/docs//346/265/213/350/257/2253.md +73 -0
- package/docs//346/265/213/350/257/2254.md +138 -0
- package/docs//346/265/213/350/257/2255.md +124 -0
- package/docs//347/254/254/344/272/214/344/273/275/345/267/241/346/243/200/346/212/245/345/221/212.md +103 -0
- package/examples/agent-tools.ts +74 -0
- package/examples/cross-host-diagnosis.ts +49 -0
- package/examples/longrun-observe.ts +46 -0
- package/package.json +47 -0
- package/src/agent-toolkit.ts +247 -0
- package/src/dialect.ts +100 -0
- package/src/driver.ts +142 -0
- package/src/events.ts +96 -0
- package/src/factory.ts +51 -0
- package/src/file-edit.ts +195 -0
- package/src/file-transfer.ts +196 -0
- package/src/index.ts +38 -0
- package/src/layer-discovery.ts +79 -0
- package/src/layer-env.ts +331 -0
- package/src/local-driver.ts +325 -0
- package/src/local-session-manager.ts +397 -0
- package/src/logger.ts +19 -0
- package/src/remote-session-manager.ts +286 -0
- package/src/runtime-stack.ts +128 -0
- package/src/session-buffer.ts +183 -0
- package/src/session-types.ts +78 -0
- package/src/shell-discovery.ts +52 -0
- package/src/ssh-helpers.ts +225 -0
- package/src/ssh-pty-driver.ts +655 -0
- package/src/stream-session-manager.ts +296 -0
- package/src/stream-window.ts +309 -0
- package/src/terminal/command-executer.ts +374 -0
- package/src/terminal/output-buffer.ts +126 -0
- package/src/terminal/session-launcher.ts +146 -0
- package/src/terminal/session-registry.ts +98 -0
- package/src/terminal/tmux-invoker.ts +65 -0
- package/src/terminal/types.ts +85 -0
- package/src/terminal-manager.ts +83 -0
- package/src/tmux-driver.ts +167 -0
- package/src/vt100-screen.ts +166 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { RuntimeDriver, FileSystemDriver, ExecOptions, ExecResult, RuntimeDescriptor } from "./driver.js";
|
|
2
|
+
import type { SessionStartOptions, SessionHandleInfo, WatchOptions, ReadLogOptions } from "./session-types.js";
|
|
3
|
+
import { type SshHop } from "./ssh-helpers.js";
|
|
4
|
+
import { RemoteSessionManager } from "./remote-session-manager.js";
|
|
5
|
+
import { type FilaxisLogger } from "./logger.js";
|
|
6
|
+
/** 层状态机(净室平移源语义):connecting → active → idle → dead / degraded(退化) */
|
|
7
|
+
export type LayerState = "connecting" | "active" | "idle" | "degraded" | "dead";
|
|
8
|
+
export declare const LAYER_IDLE_THRESHOLD_MS = 10000;
|
|
9
|
+
export declare function resolveLayerState(d: {
|
|
10
|
+
alive: boolean;
|
|
11
|
+
reconnecting?: boolean;
|
|
12
|
+
execInFlight?: boolean;
|
|
13
|
+
lastActivityAt?: number;
|
|
14
|
+
shellGone?: string;
|
|
15
|
+
}, now?: number): LayerState;
|
|
16
|
+
export interface SshPtyDriverOptions {
|
|
17
|
+
logger?: FilaxisLogger;
|
|
18
|
+
}
|
|
19
|
+
/** SSH Transport driver:每层一条独立多跳 PTY + 双通道 exec + 远端会话/文件 */
|
|
20
|
+
export declare class SshPtyDriver implements RuntimeDriver {
|
|
21
|
+
readonly descriptor: RuntimeDescriptor;
|
|
22
|
+
readonly fs: FileSystemDriver;
|
|
23
|
+
readonly sessions: RemoteSessionManager;
|
|
24
|
+
private link;
|
|
25
|
+
private alive;
|
|
26
|
+
effectiveUser?: string;
|
|
27
|
+
layerShellGone?: string;
|
|
28
|
+
layerState: LayerState;
|
|
29
|
+
lastActivityAt: number;
|
|
30
|
+
execInFlight: boolean;
|
|
31
|
+
reconnecting: boolean;
|
|
32
|
+
private reconnectAttempts;
|
|
33
|
+
private readonly log;
|
|
34
|
+
private static readonly MAX_RECONNECT;
|
|
35
|
+
private static readonly RECONNECT_DELAYS_MS;
|
|
36
|
+
constructor(descriptor: RuntimeDescriptor, opts?: SshPtyDriverOptions);
|
|
37
|
+
currentState(): LayerState;
|
|
38
|
+
/** 建链:root PTY + 逐跳登录(sshHops)+ enterSteps 有序管线 + cwd 恢复 + 身份探测 */
|
|
39
|
+
init(): Promise<void>;
|
|
40
|
+
private assertNotFallenBack;
|
|
41
|
+
private scheduleReconnect;
|
|
42
|
+
private bindLink;
|
|
43
|
+
/** 派生 ssh 子层(独立连接):本层路径为前缀,enter 管线显式清空 */
|
|
44
|
+
pushChild(hop: SshHop): Promise<SshPtyDriver>;
|
|
45
|
+
/** 派生组合层(docker-in-ssh 等):父层管线全量继承再追加本步 */
|
|
46
|
+
pushInto(enterCmd: string, enterLabel: string): Promise<SshPtyDriver>;
|
|
47
|
+
exit(): Promise<void>;
|
|
48
|
+
destroy(): Promise<void>;
|
|
49
|
+
getCwd(): Promise<string>;
|
|
50
|
+
getEnv(key: string): Promise<string | undefined>;
|
|
51
|
+
/** 双通道 exec 分发(FEAT-03) */
|
|
52
|
+
exec(command: string, options?: ExecOptions): Promise<ExecResult>;
|
|
53
|
+
/**
|
|
54
|
+
* exec 通道(非 PTY,FEAT-03):独立 ssh 进程(无 -tt),stdout/stderr 物理分离;
|
|
55
|
+
* enterSteps 以 stdin 管道重放(`… shell -s` 链,零嵌套引用)+ 层 cwd 拼装 +
|
|
56
|
+
* nonce marker 实测 effectiveUser(子 shell + $? 变量锁)。
|
|
57
|
+
*/
|
|
58
|
+
private execChannel;
|
|
59
|
+
execSession(command: string, opts?: SessionStartOptions): Promise<SessionHandleInfo>;
|
|
60
|
+
tailSession(sessionId: string, lines?: number): Promise<string[]>;
|
|
61
|
+
readSessionLog(sessionId: string, opts?: ReadLogOptions): Promise<string>;
|
|
62
|
+
watchSession(sessionId: string, opts: WatchOptions): {
|
|
63
|
+
cancel(): void;
|
|
64
|
+
};
|
|
65
|
+
stopSession(sessionId: string): Promise<{
|
|
66
|
+
exitCode?: number | null;
|
|
67
|
+
}>;
|
|
68
|
+
listSessions(): {
|
|
69
|
+
sessionId: string;
|
|
70
|
+
command: string;
|
|
71
|
+
running: boolean;
|
|
72
|
+
startedAt: number;
|
|
73
|
+
}[];
|
|
74
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { type ChildProcess } from "node:child_process";
|
|
2
|
+
import { type SessionLine } from "./session-buffer.js";
|
|
3
|
+
import type { SessionEvent } from "./session-types.js";
|
|
4
|
+
import { type FilaxisLogger } from "./logger.js";
|
|
5
|
+
export interface StreamStartOptions {
|
|
6
|
+
name?: string;
|
|
7
|
+
ownerSessionId?: string;
|
|
8
|
+
/** 建立即挂 watcher(真流式命中——无需等轮询) */
|
|
9
|
+
watchPattern?: string;
|
|
10
|
+
watchOnce?: boolean;
|
|
11
|
+
contextLines?: number;
|
|
12
|
+
/** 观察窗流桥:每行对象原样回调(截断/入窗由窗侧自理) */
|
|
13
|
+
onLine?: (line: SessionLine) => void;
|
|
14
|
+
/** 流进程退出回调(观察窗 stale 语义) */
|
|
15
|
+
onExit?: (exitCode: number | null) => void;
|
|
16
|
+
/**
|
|
17
|
+
* TUI 屏桥(第五季灵感):原始 chunk(含 ANSI 转义)喂 Vt100Screen 后回调
|
|
18
|
+
* snapshot(每 chunk 一帧——ncurses 增量包一包一帧)。观察 watch/htop/nvtop
|
|
19
|
+
* 这类无换行的全屏重绘流用 onScreen 截屏,行流用 onLine——两桥可并存。
|
|
20
|
+
*/
|
|
21
|
+
onScreen?: (snap: string, frames: number) => void;
|
|
22
|
+
}
|
|
23
|
+
export type SpawnImpl = (cmd: string, argv: string[], opts: {
|
|
24
|
+
stdio: unknown[];
|
|
25
|
+
}) => ChildProcess;
|
|
26
|
+
export interface StreamSessionManagerOptions {
|
|
27
|
+
/** 禁用 ControlMaster(测试/平台受限) */
|
|
28
|
+
disableMux?: boolean;
|
|
29
|
+
/** 无人消费且无 armed watcher 的回收 TTL(默认 30min;测试可注入短值) */
|
|
30
|
+
idleReapMs?: number;
|
|
31
|
+
/** spawn 注入(缺省 node:child_process.spawn)——mock 全测关键 */
|
|
32
|
+
spawnImpl?: SpawnImpl;
|
|
33
|
+
/** mux socket 目录(缺省 os.tmpdir()/filaxis-stream-mux) */
|
|
34
|
+
muxDir?: string;
|
|
35
|
+
logger?: FilaxisLogger;
|
|
36
|
+
}
|
|
37
|
+
export declare class StreamSessionManager {
|
|
38
|
+
onEvent: (ev: SessionEvent) => void;
|
|
39
|
+
private entries;
|
|
40
|
+
private readonly destKey;
|
|
41
|
+
private readonly destArgs;
|
|
42
|
+
private readonly muxEnabled;
|
|
43
|
+
private readonly idleReapMs?;
|
|
44
|
+
private readonly spawnImpl;
|
|
45
|
+
private readonly muxDir;
|
|
46
|
+
private readonly log;
|
|
47
|
+
private static readonly DEFAULT_IDLE_REAP_MS;
|
|
48
|
+
constructor(destArgs: string[], opts?: StreamSessionManagerOptions);
|
|
49
|
+
/** ControlMaster flags:auto + Persist(首条流顺带建立主连接,无独立 -MNf 步骤) */
|
|
50
|
+
private muxFlags;
|
|
51
|
+
private ensureMuxDir;
|
|
52
|
+
/** 建立观察流:真流式子进程,stdout/stderr 双流逐行进 ring/watcher */
|
|
53
|
+
start(command: string, opts?: StreamStartOptions): Promise<{
|
|
54
|
+
sessionId: string;
|
|
55
|
+
name?: string;
|
|
56
|
+
}>;
|
|
57
|
+
/** 流式 watcher:行到达即匹配(真流,无轮询间隔) */
|
|
58
|
+
watch(sessionId: string, opts: {
|
|
59
|
+
pattern: string | RegExp;
|
|
60
|
+
once?: boolean;
|
|
61
|
+
contextLines?: number;
|
|
62
|
+
}): {
|
|
63
|
+
cancel(): void;
|
|
64
|
+
};
|
|
65
|
+
tail(sessionId: string, n?: number): string[];
|
|
66
|
+
/** 行对象视图(带 stream 标记——FEAT-05 按流过滤) */
|
|
67
|
+
tailLines(sessionId: string, n?: number): SessionLine[];
|
|
68
|
+
stop(sessionId: string): void;
|
|
69
|
+
list(): Array<{
|
|
70
|
+
sessionId: string;
|
|
71
|
+
name?: string;
|
|
72
|
+
command: string;
|
|
73
|
+
running: boolean;
|
|
74
|
+
idleMs: number;
|
|
75
|
+
armedWatchers: number;
|
|
76
|
+
}>;
|
|
77
|
+
/** 注意力衰减回收(时钟可注入——测试用);armed watcher = 常驻注意力不回收 */
|
|
78
|
+
reapIdle(now?: number, ttlMs?: number): void;
|
|
79
|
+
private onChunk;
|
|
80
|
+
private releaseMaster;
|
|
81
|
+
private require;
|
|
82
|
+
private emit;
|
|
83
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* stream-window.ts — 流式观察窗(远端拉模式流反转为本地推模式滚动窗)
|
|
3
|
+
*
|
|
4
|
+
* 净室平移自 Aethelum-Nexus `src/core/stream/stream-window.ts`
|
|
5
|
+
* (源锚 8977ff3)。OSS 化:logger 注入;引擎接 filaxis StreamSessionManager
|
|
6
|
+
* (onLine 行对象→text 投影);local 窗 spawn 弃 shell:true(Windows cmd 会挂
|
|
7
|
+
* unix 命令)改 shell -c 与 exec 一致。
|
|
8
|
+
*
|
|
9
|
+
* 关键语义(源施工图定案,全保):
|
|
10
|
+
* - 帧编号 per-window 单调递增,断连续编不归零(断点续读正确性依赖)
|
|
11
|
+
* - 双限缓冲:帧数环形覆盖(滚动=设计行为)+ 单帧字节截断
|
|
12
|
+
* - peek 三模式(last / window 时间窗 / since 增量),全部非破坏读
|
|
13
|
+
* - 多消费者游标:consumer_id → last_seq,互不干扰
|
|
14
|
+
* - 断流 stale 窗保留全部帧;重连续编;关窗收割流进程
|
|
15
|
+
* - 窗纯内存缓冲(不走消息路由——消费方自行桥接)
|
|
16
|
+
*/
|
|
17
|
+
import { type ChildProcess } from "node:child_process";
|
|
18
|
+
import { StreamSessionManager } from "./stream-session-manager.js";
|
|
19
|
+
import { type FilaxisLogger } from "./logger.js";
|
|
20
|
+
export interface StreamFrame {
|
|
21
|
+
seq: number;
|
|
22
|
+
/** 帧到达本地时刻(时钟域统一本地) */
|
|
23
|
+
ts: number;
|
|
24
|
+
text: string;
|
|
25
|
+
}
|
|
26
|
+
export interface PeekResult {
|
|
27
|
+
name: string;
|
|
28
|
+
oldestSeq: number;
|
|
29
|
+
newestSeq: number;
|
|
30
|
+
stale: boolean;
|
|
31
|
+
frames: StreamFrame[];
|
|
32
|
+
/** since 模式且中间发生环形覆盖时置 true(空洞显式可见,不静默) */
|
|
33
|
+
gap?: boolean;
|
|
34
|
+
}
|
|
35
|
+
type Transport = {
|
|
36
|
+
kind: "local";
|
|
37
|
+
child: ChildProcess;
|
|
38
|
+
} | {
|
|
39
|
+
kind: "remote";
|
|
40
|
+
manager: StreamSessionManager;
|
|
41
|
+
sessionId: string;
|
|
42
|
+
};
|
|
43
|
+
export declare class StreamWindow {
|
|
44
|
+
readonly name: string;
|
|
45
|
+
readonly layerAlias: string;
|
|
46
|
+
readonly source: string;
|
|
47
|
+
readonly command: string;
|
|
48
|
+
private readonly maxFrames;
|
|
49
|
+
private readonly maxLineBytes;
|
|
50
|
+
private frames;
|
|
51
|
+
private nextSeq;
|
|
52
|
+
private _stale;
|
|
53
|
+
private _closed;
|
|
54
|
+
private readonly consumers;
|
|
55
|
+
private transport;
|
|
56
|
+
private lastActivityAt;
|
|
57
|
+
constructor(name: string, layerAlias: string, source: string, command: string, maxFrames?: number, maxLineBytes?: number);
|
|
58
|
+
get stale(): boolean;
|
|
59
|
+
get closed(): boolean;
|
|
60
|
+
get frameCount(): number;
|
|
61
|
+
get oldestSeq(): number;
|
|
62
|
+
get newestSeq(): number;
|
|
63
|
+
get lastActiveAt(): number;
|
|
64
|
+
/** 行入窗(截断+环形覆盖;不更新消费者游标——游标只在 peek since 时推进) */
|
|
65
|
+
push(text: string): void;
|
|
66
|
+
/** 最近 n 帧 */
|
|
67
|
+
peekLast(n?: number): PeekResult;
|
|
68
|
+
/** 最近 ms 毫秒内的帧 */
|
|
69
|
+
peekWindow(ms: number): PeekResult;
|
|
70
|
+
/**
|
|
71
|
+
* 增量断点续读:返回 > sinceSeq 的帧(上限 n 兜底)。
|
|
72
|
+
* 带 consumerId 时推进该消费者游标;中间覆盖(oldestSeq > sinceSeq+1)置 gap。
|
|
73
|
+
*/
|
|
74
|
+
peekSince(sinceSeq: number, n?: number, consumerId?: string): PeekResult;
|
|
75
|
+
/** 无状态读取消费者已知游标(未注册返回 0) */
|
|
76
|
+
consumerSeq(consumerId: string): number;
|
|
77
|
+
/** 流进程退出/断连 → stale(保留全部帧,seq 冻结在 nextSeq 上,重连续编) */
|
|
78
|
+
markStale(): void;
|
|
79
|
+
attachTransport(t: Transport): void;
|
|
80
|
+
/** 关窗:收割流进程 + 内容清空(由 Registry 除名) */
|
|
81
|
+
close(): void;
|
|
82
|
+
private snapshot;
|
|
83
|
+
}
|
|
84
|
+
export interface StreamWindowRegistryOptions {
|
|
85
|
+
/** 空引用窗延迟销毁 TTL(对齐 stream 注意力衰减语义) */
|
|
86
|
+
idleTtlMs?: number;
|
|
87
|
+
/** local 窗 shell(缺省 resolveLocalShell——与 exec 同源) */
|
|
88
|
+
shell?: string;
|
|
89
|
+
logger?: FilaxisLogger;
|
|
90
|
+
}
|
|
91
|
+
export declare class StreamWindowRegistry {
|
|
92
|
+
private readonly windows;
|
|
93
|
+
private readonly sweeper;
|
|
94
|
+
private readonly idleTtlMs;
|
|
95
|
+
private readonly shell;
|
|
96
|
+
private readonly log;
|
|
97
|
+
constructor(opts?: StreamWindowRegistryOptions);
|
|
98
|
+
/** 开窗(local 经 spawn shell -c;remote 经 StreamSessionManager 行桥) */
|
|
99
|
+
open(opts: {
|
|
100
|
+
name: string;
|
|
101
|
+
layerAlias?: string;
|
|
102
|
+
command: string;
|
|
103
|
+
maxFrames?: number;
|
|
104
|
+
maxLineBytes?: number;
|
|
105
|
+
manager?: StreamSessionManager;
|
|
106
|
+
}): Promise<StreamWindow>;
|
|
107
|
+
stop(name: string): void;
|
|
108
|
+
/** 通配聚合 peek:glob 如 "vm-ai:*"(* 与 ?;无通配=精确名) */
|
|
109
|
+
peek(pattern: string): StreamWindow[];
|
|
110
|
+
get(name: string): StreamWindow | undefined;
|
|
111
|
+
list(): Array<{
|
|
112
|
+
name: string;
|
|
113
|
+
layer: string;
|
|
114
|
+
frames: number;
|
|
115
|
+
stale: boolean;
|
|
116
|
+
command: string;
|
|
117
|
+
}>;
|
|
118
|
+
private require;
|
|
119
|
+
private matches;
|
|
120
|
+
private sweep;
|
|
121
|
+
dispose(): void;
|
|
122
|
+
}
|
|
123
|
+
export declare function getStreamWindowRegistry(): StreamWindowRegistry;
|
|
124
|
+
export {};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { type BufferStats } from "./output-buffer.js";
|
|
2
|
+
import { type PromptPayload, type PromptResponder, type TerminalConfig } from "./types.js";
|
|
3
|
+
import { type FilaxisLogger } from "../logger.js";
|
|
4
|
+
export interface ProgressSnapshot {
|
|
5
|
+
tail: string;
|
|
6
|
+
stats: BufferStats;
|
|
7
|
+
}
|
|
8
|
+
export interface RunOptions {
|
|
9
|
+
timeoutMs?: number;
|
|
10
|
+
/** 交互响应者(密码/确认 Prompt——消费方注入;null=无交互能力) */
|
|
11
|
+
responder?: PromptResponder | null;
|
|
12
|
+
/** 流式回调:每次 capture 到新内容触发(原始 tail 快照,未剥 marker 噪声) */
|
|
13
|
+
onProgress?: (snapshot: ProgressSnapshot) => void;
|
|
14
|
+
}
|
|
15
|
+
export interface TerminalExecResult {
|
|
16
|
+
stdout: string;
|
|
17
|
+
exitCode: number;
|
|
18
|
+
}
|
|
19
|
+
/** 命令失败富错误:partialOutput 携带截至失败时刻的累积输出 */
|
|
20
|
+
export declare class CommandExecError extends Error {
|
|
21
|
+
readonly partialOutput: string;
|
|
22
|
+
/** 超时/中断:远端终态未核实("unknown")——上层按未知处理,严禁当已清理 */
|
|
23
|
+
outcome?: "unknown";
|
|
24
|
+
constructor(message: string, partialOutput?: string, outcome?: "unknown");
|
|
25
|
+
}
|
|
26
|
+
/** 二进制嗅探:前 N 字节 null 占比 >10% 判 binary(PTY 偶发 NUL 容忍) */
|
|
27
|
+
export declare function isBinaryChunk(text: string): boolean;
|
|
28
|
+
export interface CommandExecuterOptions {
|
|
29
|
+
config?: Partial<TerminalConfig>;
|
|
30
|
+
logger?: FilaxisLogger;
|
|
31
|
+
}
|
|
32
|
+
export declare class CommandExecuter {
|
|
33
|
+
private constructor();
|
|
34
|
+
private static cfg;
|
|
35
|
+
/** 已关回显的会话(每会话一次性 stty -echo——交互 shell 的命令回显不进 stdout) */
|
|
36
|
+
private static sttyOff;
|
|
37
|
+
/** per-session 命令链(审查问题 2:同会话命令必须串行——并发 send-keys 交错) */
|
|
38
|
+
private static chains;
|
|
39
|
+
/** 因终态不可确认被销毁的会话(自愈:重建后 has-session 探活即清除标记) */
|
|
40
|
+
private static deadSessions;
|
|
41
|
+
private static log;
|
|
42
|
+
/** 配置注入(SDK 组装期一次性;测试可覆盖) */
|
|
43
|
+
static configure(opts?: CommandExecuterOptions): void;
|
|
44
|
+
/**
|
|
45
|
+
* 在 tmux 会话中执行命令(四阶段 temporal isolation——审查问题 2 修复):
|
|
46
|
+
* - **per-session 串行**:同会话命令排队,绝不并发 send-keys;
|
|
47
|
+
* - 超时(墙钟/不活动)= INTERRUPTING:发 C-c 杀远端前台任务,调用方立即
|
|
48
|
+
* 收 CommandExecError(挂 outcome:"unknown")——但**会话锁不释放**;
|
|
49
|
+
* - DRAINING(仍在链内):等自己的 marker 可信终态(上限 5s)才把会话交还
|
|
50
|
+
* 下一条命令;
|
|
51
|
+
* - 等不到终态 → kill-session 销毁 + 标记 dead(后续调用显式报错指引重建;
|
|
52
|
+
* 重建后探活自愈)——绝不定时强行复用。
|
|
53
|
+
*/
|
|
54
|
+
static execute(id: string, command: string, opts?: RunOptions): Promise<TerminalExecResult>;
|
|
55
|
+
private static executeLocked;
|
|
56
|
+
/** 交互 Prompt 检测(末尾锚定,避免输出中段字样误判) */
|
|
57
|
+
static detectPrompt(paneText: string, terminalId: string): PromptPayload | null;
|
|
58
|
+
private static parseExitCode;
|
|
59
|
+
/** 增量 delta:新快照以上次为前缀 → 尾部;归一 \r 后再试;漂移则整屏(宁重复不丢) */
|
|
60
|
+
private static computeDelta;
|
|
61
|
+
/** 剥 marker/echo 噪声得纯净 stdout(marker 行起全停) */
|
|
62
|
+
private static stripMarkerNoise;
|
|
63
|
+
private static extractPartialOutput;
|
|
64
|
+
private static sendKeys;
|
|
65
|
+
/**
|
|
66
|
+
* DRAINING(审查问题 2):超时 ^C 后等可信回提示符证据(有上限)。
|
|
67
|
+
* 两凭证任一先到:① 原命令 marker(^C 后 trap/echo 幸存时)② 独立探测哨兵
|
|
68
|
+
* drainToken(^C 之后新发的 echo——^C 杀整行 job 也不影响它)。
|
|
69
|
+
* 链内调用——串行保证下一条命令在本方法返回/超时后才接手会话。
|
|
70
|
+
*/
|
|
71
|
+
private static waitForDrain;
|
|
72
|
+
private static capturePane;
|
|
73
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* terminal/output-buffer.ts — 三级输出缓冲(净室平移,源锚 8977ff3,纯逻辑零 I/O)
|
|
3
|
+
*
|
|
4
|
+
* pending(增量数组)/ aggregated(留尾串)/ tail(快照)三级;游标读取以
|
|
5
|
+
* UTF-16 码元绝对偏移计,封顶裁头显式报断档(validFrom/lostChars)——
|
|
6
|
+
* 拒绝静默返回不连续内容。
|
|
7
|
+
*/
|
|
8
|
+
import type { OutputStream } from "./types.js";
|
|
9
|
+
export interface BufferStats {
|
|
10
|
+
total: number;
|
|
11
|
+
truncated: boolean;
|
|
12
|
+
}
|
|
13
|
+
export type ReadLogResult = {
|
|
14
|
+
ok: boolean;
|
|
15
|
+
text: string;
|
|
16
|
+
nextOffset: number;
|
|
17
|
+
validFrom?: number;
|
|
18
|
+
lostChars?: number;
|
|
19
|
+
};
|
|
20
|
+
export interface BufferCaps {
|
|
21
|
+
pendingChars: number;
|
|
22
|
+
aggregatedChars: number;
|
|
23
|
+
tailChars: number;
|
|
24
|
+
}
|
|
25
|
+
export declare class OutputBuffer {
|
|
26
|
+
private pendingStdout;
|
|
27
|
+
private pendingStderr;
|
|
28
|
+
private aggregated;
|
|
29
|
+
private tail;
|
|
30
|
+
private totalOutputChars;
|
|
31
|
+
private truncated;
|
|
32
|
+
private baseOffset;
|
|
33
|
+
private readonly pendingCap;
|
|
34
|
+
private readonly aggregatedCap;
|
|
35
|
+
private readonly tailCap;
|
|
36
|
+
constructor(caps: BufferCaps);
|
|
37
|
+
append(chunk: string, stream?: OutputStream): void;
|
|
38
|
+
/** 取走并清空未读增量 */
|
|
39
|
+
drainPending(): string;
|
|
40
|
+
readTail(): string;
|
|
41
|
+
/** 分页读取(绝对偏移游标;游标过期显式报断档) */
|
|
42
|
+
readLog(offset?: number, limit?: number): ReadLogResult;
|
|
43
|
+
/** 便捷视图:丢失时前置断档提示行 */
|
|
44
|
+
readLogText(offset?: number, limit?: number): string;
|
|
45
|
+
get stats(): BufferStats;
|
|
46
|
+
hasPending(): boolean;
|
|
47
|
+
reset(): void;
|
|
48
|
+
private trimPending;
|
|
49
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* terminal/session-launcher.ts — 持久 tmux 会话启动器
|
|
3
|
+
* (净室平移,源锚 8977ff3;TerminalConfig 注入化)
|
|
4
|
+
*
|
|
5
|
+
* FR-1:customId 命中存活会话 → 复用返回同 id(跨进程重启恢复连接的语义);
|
|
6
|
+
* 未知 driver 且无 launchCmd → 抛 /Unknown driver/;会话立即死亡 → 清 profile 抛带 hint 错。
|
|
7
|
+
* FR-9 第一道防线:launchCmd 前置 env_hardening_prefix 关交互 Prompt。
|
|
8
|
+
*/
|
|
9
|
+
import { SessionRegistry } from "./session-registry.js";
|
|
10
|
+
import { type TerminalConfig } from "./types.js";
|
|
11
|
+
import { type FilaxisLogger } from "../logger.js";
|
|
12
|
+
export interface SessionLauncherOptions {
|
|
13
|
+
config?: Partial<TerminalConfig>;
|
|
14
|
+
logger?: FilaxisLogger;
|
|
15
|
+
}
|
|
16
|
+
export declare class SessionLauncher {
|
|
17
|
+
private readonly registry;
|
|
18
|
+
private readonly cfg;
|
|
19
|
+
private readonly log;
|
|
20
|
+
constructor(registry: SessionRegistry, opts?: SessionLauncherOptions);
|
|
21
|
+
/** 启动(或复用)一个持久 tmux 会话;返回存活会话 id */
|
|
22
|
+
launch(driver: string, target: string | undefined, config?: Record<string, unknown>, customId?: string): Promise<string>;
|
|
23
|
+
/** 已知 driver 快捷方式渲染(与 RuntimeDescriptor 注释一致) */
|
|
24
|
+
resolveLaunchCmd(driver: string, target: string | undefined, config?: Record<string, unknown>): string;
|
|
25
|
+
/** 存活轮询(远端 driver 更长 deadline + dead-streak 容忍瞬时抖动) */
|
|
26
|
+
private waitForAlive;
|
|
27
|
+
private diedHint;
|
|
28
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type FilaxisLogger } from "../logger.js";
|
|
2
|
+
export interface TerminalProfile {
|
|
3
|
+
id: string;
|
|
4
|
+
driver: string;
|
|
5
|
+
target: string;
|
|
6
|
+
config?: Record<string, unknown>;
|
|
7
|
+
createdAt: number;
|
|
8
|
+
}
|
|
9
|
+
export type DirResolver = () => string;
|
|
10
|
+
export declare class SessionRegistry {
|
|
11
|
+
private readonly dirResolver;
|
|
12
|
+
private readonly log;
|
|
13
|
+
private reconciledDirs;
|
|
14
|
+
constructor(dirResolver: DirResolver, log?: FilaxisLogger);
|
|
15
|
+
ensureDir(): Promise<void>;
|
|
16
|
+
/** 确保目录存在 + 每目录一次重连扫描(幂等;换目录=新一轮启动) */
|
|
17
|
+
init(): Promise<void>;
|
|
18
|
+
save(profile: TerminalProfile): Promise<void>;
|
|
19
|
+
/** 缺失返回 null(不抛) */
|
|
20
|
+
load(id: string): Promise<TerminalProfile | null>;
|
|
21
|
+
/** 幂等删除 */
|
|
22
|
+
remove(id: string): Promise<void>;
|
|
23
|
+
/** 启动重连扫描:存活会话保留(=重连),僵尸 profile 清理 */
|
|
24
|
+
reconcileOnStartup(): Promise<{
|
|
25
|
+
scanned: number;
|
|
26
|
+
kept: number;
|
|
27
|
+
removed: number;
|
|
28
|
+
}>;
|
|
29
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { TmuxResult } from "./types.js";
|
|
2
|
+
export declare class TmuxInvoker {
|
|
3
|
+
static invoke(args: string[]): Promise<TmuxResult>;
|
|
4
|
+
/** 软失败变体:非零退出码返回带 exitCode 的结果(has-session 的 1=不存在是合法语义) */
|
|
5
|
+
static tryInvoke(args: string[]): Promise<TmuxResult>;
|
|
6
|
+
/** tmux 是否可用(spawn 失败=未安装) */
|
|
7
|
+
static isAvailable(): Promise<boolean>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* terminal/types.ts — 持久会话子系统共享类型与默认配置
|
|
3
|
+
*
|
|
4
|
+
* 净室平移自 Aethelum-Nexus `src/core/tools/terminal/{types,config}.ts`
|
|
5
|
+
* (源锚 8977ff3)。OSS 化:ConfigLoader/Zod schema 依赖剥——TerminalConfig
|
|
6
|
+
* 变为注入式选项 + 本文件默认值(内核不绑任何配置装载器)。
|
|
7
|
+
*/
|
|
8
|
+
/** tmux CLI 单次调用结果(stdout/stderr 严格分离;非零退出码 reject 不吞错) */
|
|
9
|
+
export interface TmuxResult {
|
|
10
|
+
stdout: string;
|
|
11
|
+
stderr: string;
|
|
12
|
+
exitCode: number;
|
|
13
|
+
}
|
|
14
|
+
export declare class TmuxError extends Error {
|
|
15
|
+
readonly args: string[];
|
|
16
|
+
readonly exitCode: number;
|
|
17
|
+
readonly stderr: string;
|
|
18
|
+
constructor(message: string, args: string[], exitCode: number, stderr: string);
|
|
19
|
+
}
|
|
20
|
+
/** 交互 Prompt 载荷(持久会话内检测到 Password/Confirm 等等待输入时) */
|
|
21
|
+
export interface PromptPayload {
|
|
22
|
+
type: "CONFIRM";
|
|
23
|
+
title: string;
|
|
24
|
+
message: string;
|
|
25
|
+
metadata: {
|
|
26
|
+
terminalId: string;
|
|
27
|
+
prompt: string;
|
|
28
|
+
platform: "terminal";
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/** 交互响应者:返回用户回复字符串,null 表示未作答(消费方自行适配 UI/IM) */
|
|
32
|
+
export interface PromptResponder {
|
|
33
|
+
ask(payload: PromptPayload): Promise<string | null>;
|
|
34
|
+
}
|
|
35
|
+
export type OutputStream = "stdout" | "stderr";
|
|
36
|
+
/** 持久会话子系统配置(注入式;默认值覆盖源 ConfigLoader schema 语义) */
|
|
37
|
+
export interface TerminalConfig {
|
|
38
|
+
/** 前置环境加固前缀(关交互入口——GIT_TERMINAL_PROMPT/SSH_ASKPASS/PAGER) */
|
|
39
|
+
env_hardening_prefix: string;
|
|
40
|
+
spawn_max_wait_ms_local: number;
|
|
41
|
+
spawn_max_wait_ms_remote: number;
|
|
42
|
+
spawn_poll_ms: number;
|
|
43
|
+
command_timeout_ms: number;
|
|
44
|
+
/** 不活动超时(只杀哑不杀慢——有新输出即重置) */
|
|
45
|
+
no_output_timeout_ms: number;
|
|
46
|
+
poll_interval_ms: number;
|
|
47
|
+
capture_scrollback: number;
|
|
48
|
+
buffer_pending_chars: number;
|
|
49
|
+
buffer_aggregated_chars: number;
|
|
50
|
+
buffer_tail_chars: number;
|
|
51
|
+
/** 交互 Prompt 检测模式(多行正则,末尾锚定由 compile 保证) */
|
|
52
|
+
prompt_patterns: string[];
|
|
53
|
+
}
|
|
54
|
+
export declare const DEFAULT_TERMINAL_CONFIG: TerminalConfig;
|
|
55
|
+
/** 把 prompt_patterns 源串编译成多行 OR 正则(末尾锚定 $;mi——大小写不敏感+多行) */
|
|
56
|
+
export declare function compilePromptPatterns(patterns: string[]): RegExp;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type TerminalProfile } from "./terminal/session-registry.js";
|
|
2
|
+
import { type RunOptions } from "./terminal/command-executer.js";
|
|
3
|
+
import type { TerminalConfig } from "./terminal/types.js";
|
|
4
|
+
import { type FilaxisLogger } from "./logger.js";
|
|
5
|
+
export type { TerminalProfile } from "./terminal/session-registry.js";
|
|
6
|
+
export interface TerminalManagerOptions {
|
|
7
|
+
/** profile 持久化目录(缺省 os.tmpdir()/filaxis-terminals) */
|
|
8
|
+
terminalDir?: string;
|
|
9
|
+
config?: Partial<TerminalConfig>;
|
|
10
|
+
logger?: FilaxisLogger;
|
|
11
|
+
}
|
|
12
|
+
export declare class TerminalManager {
|
|
13
|
+
private readonly registry;
|
|
14
|
+
private readonly launcher;
|
|
15
|
+
readonly terminalDir: string;
|
|
16
|
+
constructor(opts?: TerminalManagerOptions);
|
|
17
|
+
/** 确保目录存在 + 一次重连扫描(幂等) */
|
|
18
|
+
init(): Promise<void>;
|
|
19
|
+
/** 启动(或复用)持久 tmux 会话 */
|
|
20
|
+
spawnSession(driver: string, target: string | undefined, config?: Record<string, unknown>, customId?: string): Promise<string>;
|
|
21
|
+
/** 会话内执行命令(纯净 stdout + exitCode;第三参 number|RunOptions 多态) */
|
|
22
|
+
runCommand(id: string, command: string, timeoutMsOrOptions?: number | RunOptions): Promise<{
|
|
23
|
+
stdout: string;
|
|
24
|
+
exitCode: number;
|
|
25
|
+
}>;
|
|
26
|
+
/** 销毁会话 + 删 profile(best-effort:kill-session 失败不阻断) */
|
|
27
|
+
destroySession(id: string): Promise<void>;
|
|
28
|
+
/** 读 profile;缺失返回 null */
|
|
29
|
+
getSession(id: string): Promise<TerminalProfile | null>;
|
|
30
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tmux-driver.ts — 持久会话 Transport 适配层
|
|
3
|
+
* (净室平移自 Aethelum-Nexus tmux-driver.ts,源锚 8977ff3)
|
|
4
|
+
*
|
|
5
|
+
* OSS 化:剥 RuntimeProcessManager/RemoteSessionManager/TmuxFileSystem——
|
|
6
|
+
* fs 经 runCommand base64(方言适配接线 M2 批);远端会话族沿用本文件
|
|
7
|
+
* nohup 会话语义(经 TerminalManager 会话 shell 执行)。
|
|
8
|
+
* terminalId 绑定 + cwd 恢复 + destroy 时 nohup 后台进程显式清理。
|
|
9
|
+
*/
|
|
10
|
+
import type { RuntimeDriver, FileSystemDriver, ExecOptions, ExecResult, RuntimeDescriptor } from "./driver.js";
|
|
11
|
+
import { TerminalManager } from "./terminal-manager.js";
|
|
12
|
+
import type { SessionStartOptions, SessionHandleInfo, WatchOptions, ReadLogOptions } from "./session-types.js";
|
|
13
|
+
import { type FilaxisLogger } from "./logger.js";
|
|
14
|
+
export interface TmuxDriverOptions {
|
|
15
|
+
terminalManager?: TerminalManager;
|
|
16
|
+
logger?: FilaxisLogger;
|
|
17
|
+
}
|
|
18
|
+
export declare class TmuxSessionDriver implements RuntimeDriver {
|
|
19
|
+
descriptor: RuntimeDescriptor;
|
|
20
|
+
fs: FileSystemDriver;
|
|
21
|
+
readonly terminal: TerminalManager;
|
|
22
|
+
private readonly log;
|
|
23
|
+
constructor(descriptor: RuntimeDescriptor, opts?: TmuxDriverOptions);
|
|
24
|
+
/** init:无 terminalId 则 spawn;有则验证存活(跨进程重连语义);cwd 恢复 */
|
|
25
|
+
init(): Promise<void>;
|
|
26
|
+
/** 本实例启动的后台会话(sessionId 集合——destroy 只清理自己的,绝不扫目录误杀他层) */
|
|
27
|
+
private ownedSessions;
|
|
28
|
+
/** destroy:只停**本实例**启动的后台进程(审查问题 1:全目录扫杀会误杀同机其他
|
|
29
|
+
* Filaxis 实例/其他层的任务——/tmp/filaxis-sessions 是共享目录),再销毁会话 */
|
|
30
|
+
destroy(): Promise<void>;
|
|
31
|
+
exec(command: string, options?: ExecOptions): Promise<ExecResult>;
|
|
32
|
+
getCwd(): Promise<string>;
|
|
33
|
+
getEnv(key: string): Promise<string | undefined>;
|
|
34
|
+
execSession(command: string, opts?: SessionStartOptions): Promise<SessionHandleInfo>;
|
|
35
|
+
tailSession(sessionId: string, lines?: number): Promise<string[]>;
|
|
36
|
+
readSessionLog(sessionId: string, opts?: ReadLogOptions): Promise<string>;
|
|
37
|
+
watchSession(_sessionId: string, _opts: WatchOptions): {
|
|
38
|
+
cancel(): void;
|
|
39
|
+
};
|
|
40
|
+
stopSession(sessionId: string): Promise<{
|
|
41
|
+
exitCode?: number | null;
|
|
42
|
+
}>;
|
|
43
|
+
listSessions(): Array<{
|
|
44
|
+
sessionId: string;
|
|
45
|
+
command: string;
|
|
46
|
+
running: boolean;
|
|
47
|
+
startedAt: number;
|
|
48
|
+
}>;
|
|
49
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vt100-screen.ts — 轻量 VT100 虚拟屏幕(TUI 全屏程序字符级截屏)
|
|
3
|
+
*
|
|
4
|
+
* 第五季观察窗专项实测灵感:`watch/htop/nvtop` 等 ncurses 程序按
|
|
5
|
+
* `\x1b[row;colH` 光标定位增量重绘、**不输出换行**——行模式对它退化(数据在但
|
|
6
|
+
* 不可读)。本件是可选的终端仿真:feed 原始字节流 → snapshot 字符级高清截屏。
|
|
7
|
+
*
|
|
8
|
+
* 纯逻辑零 I/O 零依赖;手写状态机零正则。转义序列子集(观察够用):
|
|
9
|
+
* CUP/HVP(光标定位)、ED/EL(清屏/清行)、CUU/CUD/CUF/CUB(光标移动)、
|
|
10
|
+
* LF(换行+滚屏)/CR/BS/TAB、SGR 忽略、OSC 忽略;其余序列保守忽略。
|
|
11
|
+
* Autowrap:超行宽折到下一行(VT100 语义——ncurses 假设终端宽=实际宽,
|
|
12
|
+
* 建议 cols 给足)。
|
|
13
|
+
*/
|
|
14
|
+
export interface Vt100ScreenOptions {
|
|
15
|
+
rows?: number;
|
|
16
|
+
cols?: number;
|
|
17
|
+
}
|
|
18
|
+
export declare class Vt100Screen {
|
|
19
|
+
private rows;
|
|
20
|
+
private curRow;
|
|
21
|
+
private curCol;
|
|
22
|
+
/** 解析状态:0=normal 1=esc 2=csi 3=osc */
|
|
23
|
+
private state;
|
|
24
|
+
private csiBuf;
|
|
25
|
+
private oscBuf;
|
|
26
|
+
private _frames;
|
|
27
|
+
constructor(opts?: Vt100ScreenOptions);
|
|
28
|
+
/** 喂原始字节流(含转义序列) */
|
|
29
|
+
feed(chunk: string): void;
|
|
30
|
+
/** 字符级高清截屏(行尾空白裁剪) */
|
|
31
|
+
snapshot(): string;
|
|
32
|
+
get cursor(): {
|
|
33
|
+
row: number;
|
|
34
|
+
col: number;
|
|
35
|
+
};
|
|
36
|
+
/** 已喂 chunk 数(帧计数——TUI 增量包一包一帧) */
|
|
37
|
+
get frames(): number;
|
|
38
|
+
private put;
|
|
39
|
+
private csiDispatch;
|
|
40
|
+
private writeChar;
|
|
41
|
+
private lineFeed;
|
|
42
|
+
private moveTo;
|
|
43
|
+
/** endCol=-1 表示到行尾;endRow 含 */
|
|
44
|
+
private clearRegion;
|
|
45
|
+
private clearAll;
|
|
46
|
+
}
|