@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
package/AGENTS.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Filaxis 包说明书(给使用方 Agent 读)
|
|
2
|
+
|
|
3
|
+
> 本文件只描述 `@syrkos/filaxis` 包本身,**不接管宿主仓库规范**——你的项目自有其 AGENTS.md。API 签名与详细参考见版本化文档(本文件不复制签名副本,防漂移)。
|
|
4
|
+
|
|
5
|
+
## 心智模型(补充:流式观察窗)
|
|
6
|
+
|
|
7
|
+
- **观察窗(Stream Window)**:远端拉模式流反转为**本地推模式滚动窗**——帧号 per-window 单调递增(断连续编不归零)、peek 三模式全非破坏、多消费者游标互不干扰、stale 窗帧保留重连续编、关窗收割流进程。窗是滚动缓冲不是无限历史(完整历史查落盘日志)。
|
|
8
|
+
|
|
9
|
+
## 心智模型
|
|
10
|
+
|
|
11
|
+
- **层(Layer)**:一个可执行环境(本机 / SSH 主机 / 容器 / 持久终端)。`stack.connect(descriptor)` 建立或复用。
|
|
12
|
+
- **会话(Session)**:长跑任务。`spawn` 即返,输出双文件落盘 + 热窗口。
|
|
13
|
+
- **观察流(Stream)**:独立 SSH channel 的真流式 tail——观察是廉价的,连接是稀缺的(自动复用主连接)。
|
|
14
|
+
- **两个身份键,两个职责**:连接拓扑指纹管「复用哪条连接」;环境身份键管「这台环境是什么」(同落点经不同跳板路径=同一环境)。
|
|
15
|
+
|
|
16
|
+
## 核心不变量(执行语义的宪法)
|
|
17
|
+
|
|
18
|
+
> **没有经过验证的远端终态,就绝不能推导出执行成功;执行结果未知,也绝不能自动重放非幂等操作。**
|
|
19
|
+
|
|
20
|
+
三条落地形态:
|
|
21
|
+
|
|
22
|
+
1. **完成哨兵是唯一凭证**:exec 的退出码只认 `__FILAXIS_META_<nonce>` 尾缀(nonce 完整匹配 + 退出码可解析 + 取尾部出现)。无哨兵(断链/远端 kill -9/abort/信号杀)一律 `reject` 附半截现场,Error 挂 `outcome: "unknown"`——绝不 resolve 伪造退出码。
|
|
23
|
+
2. **Abort ≠ 远端已取消**:abort/超时杀的是本地 ssh 进程,远端进程树状态未核实——`AbortError` 同时挂 `outcome: "unknown"`,上层须按未知处理(核实或上报),严禁直接推导远端已停。
|
|
24
|
+
3. **内核零自动重放**:任何失败/未知路径都不自动重试非幂等命令——重放决策属上层(拿到 `outcome=unknown` 后先核实再决策)。
|
|
25
|
+
4. **PTY 会话是有状态资源**:任务 Promise 的生命周期与 PTY 会话可复用生命周期必须分离——当前命令终态未经可信确认(自己 nonce 的 $? 行或 ^C trap 行),同一条 PTY 不得交给下一条命令(四阶段 RUNNING/INTERRUPTING/DRAINING/IDLE;确认不了就销毁重建,绝不定时强行复用)。
|
|
26
|
+
|
|
27
|
+
## 防误用契约(用错就是生产事故的六条)
|
|
28
|
+
|
|
29
|
+
1. **终止即请求、死亡须核实**:`stop()` 返回 `{status: "exited" | "stopped" | "unknown"}`。`unknown`(断线/分区/核实超时)**不是成功也不是失败**——严禁当已清理继续往下走;应重试核实或显式上报。
|
|
30
|
+
2. **退出码是数据**:`exec` 非零退出码**不抛异常**(如实在 `exitCode` 字段);只有「无法执行」(超时/abort/连接死)才 reject。别把两者混为一谈。
|
|
31
|
+
3. **`ready()` 有窗口语义**:先回溯热窗口再等增量——spawn 与 ready 之间输出的标记不会漏;若报「标记可能已滚出」,查落盘日志,别盲目重试。
|
|
32
|
+
4. **通道语义**:缺省 `channel:"exec"`(stdout/stderr 分离、层上下文拼装);`channel:"pty"` 继承层内交互状态(su/cd),但 **stderr 混流是终端物理语义**,不是 bug。多跳 PTY 链与交互式二次跳转对 exec 通道**显式拒绝**——这是诚实行为,换 pty 或把途径机收敛为 `-J`。
|
|
33
|
+
5. **跨流顺序**:双文件落盘后的聚合序是**本地到达序**(近似),不承诺进程真实写入序。
|
|
34
|
+
6. **方言适配自动进行**:base64/sha256 按连接探测缓存(OPNsense/Alpine 形态已测)——但你自己的远端脚本原语若依赖 GNU 方言,请同样探测,勿硬编码。
|
|
35
|
+
|
|
36
|
+
## 环境规格与漂移
|
|
37
|
+
|
|
38
|
+
探测结果只存蒸馏值(版本/有无/型号);`upsert` 指纹不一致会标 **stale 且不覆盖**——重探是显式决定。nvidia-smi 驱动不通信的失败文案**不会**被记成 GPU 型号。
|
|
39
|
+
|
|
40
|
+
## 安全边界
|
|
41
|
+
|
|
42
|
+
host key 缺省 TOFU(accept-new);高价值环境请配 `hostKeyPolicy:"strict"` + `knownHostsFile`。凭据只传**路径**;所有渲染与日志永不含密钥内容。高危操作授权属你的策略层,本包不越权预设。
|
|
43
|
+
|
|
44
|
+
## 能力探测
|
|
45
|
+
|
|
46
|
+
部署环境缺什么,`await stack.features()` 说了算(`mux/sftp/persistent/transports/stderrSeparated`)——降级不靠猜。
|
package/LICENSE
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright [yyyy] [name of copyright owner]
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
package/NOTICE
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Syrkos Filaxis (牵丝)
|
|
2
|
+
Copyright 2026 Corolin (YiCheng Ji) and Syrkos Contributors.
|
|
3
|
+
|
|
4
|
+
This product includes software developed as part of the Syrkos (漩核) project.
|
|
5
|
+
|
|
6
|
+
"Syrkos", "漩核", "Filaxis", and "牵丝" are brand names claimed by Corolin (YiCheng Ji).
|
|
7
|
+
|
|
8
|
+
The Apache License, Version 2.0, does not grant trademark rights except as provided in Section 6 of that license.
|
|
9
|
+
|
|
10
|
+
All trademark rights, if any, are reserved by their respective owners.
|
package/README.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# 牵丝 Filaxis
|
|
2
|
+
|
|
3
|
+
**千里一线,执行有迹。—— Connect · Execute · Observe · Reattach**
|
|
4
|
+
|
|
5
|
+
面向自动化与 Agent 的远程执行运行时 SDK(Syrkos · 漩核旗下)。TypeScript 编写,Bun 与 Node LTS 双运行时,Windows 与 Linux 同构。
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { RuntimeStack } from "@syrkos/filaxis";
|
|
9
|
+
|
|
10
|
+
const stack = new RuntimeStack();
|
|
11
|
+
const host = await stack.connect({ driver: "ssh", target: "my-server" });
|
|
12
|
+
|
|
13
|
+
// exec:退出码是数据,失败是异常;stdout/stderr 物理分离
|
|
14
|
+
const r = await host.exec("uname -a", { timeoutMs: 10_000 });
|
|
15
|
+
console.log(r.stdout, r.stderr, r.exitCode, r.effectiveUser);
|
|
16
|
+
|
|
17
|
+
// 长跑会话:spawn 即返 → 就绪握手 → 双流观察 → watcher → 终态核实
|
|
18
|
+
const cap = await host.execSession("tcpdump -i eth0 -c 100 -w /tmp/x.pcap");
|
|
19
|
+
await host.sessions /* tail / watch / stop——见示例 */;
|
|
20
|
+
|
|
21
|
+
// 事件(零消息队列依赖——消费方自行接队列/看板)
|
|
22
|
+
stack.events.on("exit", (e) => console.log("session", e.sessionId, "exit", e.exitCode));
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## 能力矩阵
|
|
26
|
+
|
|
27
|
+
| 能力 | 说明 |
|
|
28
|
+
|---|---|
|
|
29
|
+
| 多 Transport 统一语义 | `local` / `ssh` / `tmux` 内置 + `registerDriver()` 开放注册;docker/k8s/teleport 经 launchCmd 接入 |
|
|
30
|
+
| exec 双通道 | 缺省非 PTY 通道(stdout/stderr 物理分离 + 层上下文拼装);`channel:"pty"` 继承层内交互状态 |
|
|
31
|
+
| 长跑会话生命周期 | spawn 即返、双文件 `.out/.err` 落盘、热窗口、行级 watcher、**终态三态** `exited/stopped/unknown` |
|
|
32
|
+
| ready() 就绪握手 | 回溯匹配热窗口(快输出进程竞态根治);滚出判定**有据**(evictedCount) |
|
|
33
|
+
| 观察流 | 独立 SSH channel 真流式直推(无轮询);ControlMaster 复用;注意力衰减回收 |
|
|
34
|
+
| 持久会话 | tmux 后端 terminalId 跨进程 re-attach + cwd 恢复 |
|
|
35
|
+
| 文件传输 | `putFile/getFile` base64 管道分块 + size/sha256 双校验 + **方言适配**(GNU/BSD/busybox——OPNsense/Alpine 不再必错) |
|
|
36
|
+
| 环境规格 | 两段式自发现(语言/GPU/关键包)蒸馏落盘;`matchLayers({needs})` 按需选层;指纹漂移标 stale |
|
|
37
|
+
| 连接治理 | 连接拓扑指纹复用(同拓扑零重建);3 次退避自动重连;层状态机与退化哨兵 |
|
|
38
|
+
| 安全边界 | host key 策略显式(tofu 缺省/strict+known_hosts);凭据只传路径永不外泄;渲染永不含密钥内容 |
|
|
39
|
+
|
|
40
|
+
## 安装与运行时
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
bun add @syrkos/filaxis # 或 npm install @syrkos/filaxis
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
- 运行时:Bun ≥1.x / Node LTS(内核统一 `node:` 标准库,零 runtime 专有 API——CI lint 断言)
|
|
47
|
+
- npm 依赖:**零**(`dependencies` 为空是 AC 门禁项,见 `bun run check:boundaries`)
|
|
48
|
+
- 系统依赖按能力分列(`stack.features()` 运行时探测):`ssh`≥7.3 全功能(`-J`)、`tmux` 仅持久会话(缺席自动降级非持久)、`sftp` 仅传输快路径
|
|
49
|
+
|
|
50
|
+
## 语义契约要点(使用前必读)
|
|
51
|
+
|
|
52
|
+
详见包内 [AGENTS.md](./AGENTS.md)——**终止即请求、死亡须核实**(`unknown` 不是失败也不是成功);退出码非零是数据不是异常;PTY 通道 stderr 混流是终端语义;跨流聚合序为到达序。
|
|
53
|
+
|
|
54
|
+
## 示例与技能剧本
|
|
55
|
+
|
|
56
|
+
- [examples/](./examples/):跨主机诊断 / 长跑观察 / Agent 工具面最小封装(三可运行示例)
|
|
57
|
+
- [docs/skills/](./docs/skills/):场景操作剧本(给人也给 Agent)
|
|
58
|
+
|
|
59
|
+
## 许可
|
|
60
|
+
|
|
61
|
+
[Apache-2.0](./LICENSE) + [NOTICE](./NOTICE)(牵丝 Filaxis / Syrkos · 漩核 品牌归属声明)。
|
package/bin/filaxis.ts
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* filaxis CLI — 极简宿主形态(第六季盲测摩擦点 ①:SDK 需要 bun -e 手拼几十行 TS——
|
|
4
|
+
* Agent 先跟宿主 Shell 引号搏斗。CLI 一行替代:filaxis exec|cat|edit|observe)
|
|
5
|
+
*
|
|
6
|
+
* 零依赖手写 argv。远端目标统一 --host(ssh);缺省 local。
|
|
7
|
+
*/
|
|
8
|
+
import { RuntimeStack, LocalRuntimeDriver, SshPtyDriver, editFile, discoverLayer } from "../src/index.js";
|
|
9
|
+
import type { RuntimeDriver } from "../src/driver.js";
|
|
10
|
+
|
|
11
|
+
const args = process.argv.slice(2);
|
|
12
|
+
const cmd = args[0];
|
|
13
|
+
|
|
14
|
+
function parseOpts(argv: string[]): {
|
|
15
|
+
host?: string; user?: string; port?: number; key?: string;
|
|
16
|
+
timeout?: number; duration?: number; stream?: string; all?: boolean;
|
|
17
|
+
positional: string[];
|
|
18
|
+
} {
|
|
19
|
+
const o: ReturnType<typeof parseOpts> = { positional: [] };
|
|
20
|
+
for (let i = 0; i < argv.length; i++) {
|
|
21
|
+
const a = argv[i]!;
|
|
22
|
+
if (a === "--") continue; // 命令分隔符(可省)
|
|
23
|
+
switch (a) {
|
|
24
|
+
case "--host": o.host = argv[++i]; break;
|
|
25
|
+
case "--user": o.user = argv[++i]; break;
|
|
26
|
+
case "--port": o.port = parseInt(argv[++i]!, 10); break;
|
|
27
|
+
case "--key": o.key = argv[++i]; break;
|
|
28
|
+
case "--timeout": o.timeout = parseInt(argv[++i]!, 10); break;
|
|
29
|
+
case "--duration": o.duration = parseInt(argv[++i]!, 10); break;
|
|
30
|
+
case "--stream": o.stream = argv[++i]; break;
|
|
31
|
+
case "--all": o.all = true; break;
|
|
32
|
+
default: o.positional.push(a);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return o;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function driverFor(opts: ReturnType<typeof parseOpts>): Promise<RuntimeDriver> {
|
|
39
|
+
if (opts.host) {
|
|
40
|
+
return new SshPtyDriver({
|
|
41
|
+
driver: "ssh", target: opts.host,
|
|
42
|
+
config: { connParams: { user: opts.user, port: opts.port, keyFile: opts.key } },
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
return new LocalRuntimeDriver();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function main() {
|
|
49
|
+
const o = parseOpts(args.slice(1));
|
|
50
|
+
const drv = await driverFor(o);
|
|
51
|
+
try {
|
|
52
|
+
switch (cmd) {
|
|
53
|
+
case "exec": {
|
|
54
|
+
const r = await drv.exec(o.positional.join(" "), { timeoutMs: o.timeout ?? 120_000 });
|
|
55
|
+
process.stdout.write(r.stdout);
|
|
56
|
+
if (r.stderr) process.stderr.write(r.stderr);
|
|
57
|
+
console.error(`\n[exit=${r.exitCode} user=${r.effectiveUser ?? "?"}]`);
|
|
58
|
+
process.exit(r.exitCode);
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
case "cat": {
|
|
62
|
+
const [p] = o.positional;
|
|
63
|
+
if (!p) throw new Error("用法: filaxis cat [--host ...] <path>");
|
|
64
|
+
process.stdout.write(await drv.fs.readFile(p));
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
case "edit": {
|
|
68
|
+
const [p, old, newStr] = o.positional;
|
|
69
|
+
if (!p || old === undefined || newStr === undefined) {
|
|
70
|
+
throw new Error("用法: filaxis edit [--host ...] <path> <old> <new> [--all]");
|
|
71
|
+
}
|
|
72
|
+
const r = await editFile(drv, p, old, newStr, { all: o.all });
|
|
73
|
+
console.log(`edited: ${r.path} 替换 ${r.occurrences} 处,${r.bytesBefore}B → ${r.bytesAfter}B`);
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
case "observe": {
|
|
77
|
+
const h = await drv.execSession(o.positional.join(" "), { name: "cli-observe" });
|
|
78
|
+
await new Promise((r) => setTimeout(r, o.duration ?? 8000));
|
|
79
|
+
const lines = (drv as unknown as {
|
|
80
|
+
sessions?: { tailLines?: (id: string, n?: number) => Array<{ stream: string; text: string }> };
|
|
81
|
+
}).sessions?.tailLines?.(h.sessionId, 500)
|
|
82
|
+
?? (await drv.tailSession(h.sessionId, 500)).map((t) => ({ stream: "stdout", text: t }));
|
|
83
|
+
const filtered = (o.stream && o.stream !== "both") ? lines.filter((l) => l.stream === o.stream) : lines;
|
|
84
|
+
for (const l of filtered) console.log(`[${l.stream}] ${l.text}`);
|
|
85
|
+
await drv.stopSession(h.sessionId).catch(() => undefined);
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
case "probe": {
|
|
89
|
+
const r = await discoverLayer(drv, { skipStage2: false });
|
|
90
|
+
console.log(JSON.stringify({ key: r.key, spec: r.spec, note: r.probeNote }, null, 2));
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
default:
|
|
94
|
+
console.error(`filaxis CLI — 千里一线,执行有迹
|
|
95
|
+
用法:
|
|
96
|
+
filaxis exec [--host u@h [--port N] [--key F]] [--timeout MS] -- <command>
|
|
97
|
+
filaxis cat [--host ...] <path>
|
|
98
|
+
filaxis edit [--host ...] <path> <old> <new> [--all] # 受控编辑(TOCTOU 锁+精确替换)
|
|
99
|
+
filaxis observe [--host ...] [--stream stderr|stdout|both] [--duration MS] -- <command>
|
|
100
|
+
filaxis probe [--host ...] # 两段式环境探测
|
|
101
|
+
缺省 --host 为 local。`);
|
|
102
|
+
process.exit(cmd ? 1 : 0);
|
|
103
|
+
}
|
|
104
|
+
} finally {
|
|
105
|
+
await drv.destroy().catch(() => undefined);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
void main().catch((err) => {
|
|
110
|
+
console.error(`[filaxis] ${(err as Error).message}`);
|
|
111
|
+
process.exit(1);
|
|
112
|
+
});
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* agent-toolkit.ts — 原生 Agent 工具集(Gemini 试用反馈 #3)
|
|
3
|
+
*
|
|
4
|
+
* 一行 `createAgentToolkit(stack)` 生成 LLM 工具面(run_command / spawn_and_observe /
|
|
5
|
+
* stop_and_verify)——OpenAI/Anthropic/Vercel AI SDK 通吃的 {tools, handlers} 双层结构:
|
|
6
|
+
* 定义层喂模型,执行层留宿主(桥到任意框架)。语义契约(退出码是数据/终态三态/
|
|
7
|
+
* 就绪握手)由内核承载,宿主零胶水。
|
|
8
|
+
*/
|
|
9
|
+
import type { RuntimeStack } from "./runtime-stack.js";
|
|
10
|
+
import type { RuntimeDriver } from "./driver.js";
|
|
11
|
+
export interface AgentToolDef {
|
|
12
|
+
type: "function";
|
|
13
|
+
function: {
|
|
14
|
+
name: string;
|
|
15
|
+
description: string;
|
|
16
|
+
parameters: Record<string, unknown>;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export interface AgentToolHandlers {
|
|
20
|
+
run_command(args: {
|
|
21
|
+
command: string;
|
|
22
|
+
timeout_ms?: number;
|
|
23
|
+
layer?: string;
|
|
24
|
+
}): Promise<{
|
|
25
|
+
stdout: string;
|
|
26
|
+
stderr: string;
|
|
27
|
+
exit_code: number;
|
|
28
|
+
effective_user?: string;
|
|
29
|
+
}>;
|
|
30
|
+
read_file(args: {
|
|
31
|
+
path: string;
|
|
32
|
+
layer?: string;
|
|
33
|
+
}): Promise<{
|
|
34
|
+
path: string;
|
|
35
|
+
content: string;
|
|
36
|
+
size: number;
|
|
37
|
+
mtime_ms: number;
|
|
38
|
+
}>;
|
|
39
|
+
edit_file(args: {
|
|
40
|
+
path: string;
|
|
41
|
+
old: string;
|
|
42
|
+
new: string;
|
|
43
|
+
all?: boolean;
|
|
44
|
+
layer?: string;
|
|
45
|
+
}): Promise<{
|
|
46
|
+
path: string;
|
|
47
|
+
occurrences: number;
|
|
48
|
+
bytes_before: number;
|
|
49
|
+
bytes_after: number;
|
|
50
|
+
}>;
|
|
51
|
+
observe_stream(args: {
|
|
52
|
+
command: string;
|
|
53
|
+
duration_ms?: number;
|
|
54
|
+
stream?: "stdout" | "stderr" | "both";
|
|
55
|
+
name?: string;
|
|
56
|
+
}): Promise<{
|
|
57
|
+
session_id: string;
|
|
58
|
+
lines: Array<{
|
|
59
|
+
stream: string;
|
|
60
|
+
text: string;
|
|
61
|
+
}>;
|
|
62
|
+
}>;
|
|
63
|
+
spawn_and_observe(args: {
|
|
64
|
+
command: string;
|
|
65
|
+
ready_marker?: string;
|
|
66
|
+
error_pattern?: string;
|
|
67
|
+
name?: string;
|
|
68
|
+
}): Promise<{
|
|
69
|
+
session_id: string;
|
|
70
|
+
log_base: string;
|
|
71
|
+
note?: string;
|
|
72
|
+
}>;
|
|
73
|
+
stop_and_verify(args: {
|
|
74
|
+
session_id: string;
|
|
75
|
+
}): Promise<{
|
|
76
|
+
status: "exited" | "stopped" | "unknown";
|
|
77
|
+
exit_code?: number | null;
|
|
78
|
+
note?: string;
|
|
79
|
+
}>;
|
|
80
|
+
}
|
|
81
|
+
export interface AgentToolkit {
|
|
82
|
+
tools: AgentToolDef[];
|
|
83
|
+
handlers: AgentToolHandlers;
|
|
84
|
+
/** 工具面绑定的层(缺省 local;远端层经 stack.connect 后传入重绑) */
|
|
85
|
+
readonly layer: Promise<RuntimeDriver>;
|
|
86
|
+
}
|
|
87
|
+
export interface AgentToolkitOptions {
|
|
88
|
+
/** 层描述符(缺省 local);也可直接传已连接的 driver */
|
|
89
|
+
descriptor?: {
|
|
90
|
+
driver: string;
|
|
91
|
+
target?: string;
|
|
92
|
+
config?: Record<string, unknown>;
|
|
93
|
+
};
|
|
94
|
+
driver?: RuntimeDriver;
|
|
95
|
+
/** 就绪握手与 watcher 轮询的超时上限 */
|
|
96
|
+
readyTimeoutMs?: number;
|
|
97
|
+
}
|
|
98
|
+
/** 生成 Agent 工具集(定义层 + 执行层;宿主桥接到任意 LLM 框架) */
|
|
99
|
+
export declare function createAgentToolkit(stack: RuntimeStack, opts?: AgentToolkitOptions): AgentToolkit;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dialect.ts — 远端命令方言适配层(FEAT-14:GNU · BSD · busybox 三族原语探测与渲染)
|
|
3
|
+
*
|
|
4
|
+
* 动机(生产实锤):`base64 -w 0` 为纯 GNU 方言——FreeBSD/OPNsense 的 base64
|
|
5
|
+
* 不认 `-w`(BSD 用 `-b`,解码历史差异 `-d`/`-D`);sha256 工具 Linux=sha256sum /
|
|
6
|
+
* FreeBSD=/sbin/sha256 -q / 精简容器=busybox 或 openssl dgst。目标环境
|
|
7
|
+
* (OPNsense/Alpine)不探测必错。探测结果**按连接缓存**(探测键由调用方持有)。
|
|
8
|
+
*/
|
|
9
|
+
import type { ExecResult } from "./driver.js";
|
|
10
|
+
export type DialectFamily = "gnu" | "bsd" | "busybox" | "openssl-fallback" | "unknown";
|
|
11
|
+
/** 一套已探测的远端原语(渲染为 argv 前缀,拼接数据参数后执行) */
|
|
12
|
+
export interface RemoteDialect {
|
|
13
|
+
family: DialectFamily;
|
|
14
|
+
/** 编码(无换行输出):["base64","-w","0"](GNU)/ ["base64"](BSD·默认不折行) */
|
|
15
|
+
base64Encode: string[];
|
|
16
|
+
/** 解码:GNU ["base64","-d"] / BSD ["base64","-D"] */
|
|
17
|
+
base64Decode: string[];
|
|
18
|
+
/** sha256 摘要:["sha256sum"] / ["sha256","-q"] / busybox ["sha256sum"] / openssl ["openssl","dgst","-sha256"] */
|
|
19
|
+
sha256: string[];
|
|
20
|
+
}
|
|
21
|
+
export type ExecFn = (command: string) => Promise<Pick<ExecResult, "stdout" | "exitCode">>;
|
|
22
|
+
/** 探测失败时的兜底:全部原语标记不可用(显式,不静默用错方言) */
|
|
23
|
+
export declare const NULL_DIALECT: RemoteDialect;
|
|
24
|
+
/** 探测远端方言(每命令独立判定;缺原语=该族标记跳过,非报错) */
|
|
25
|
+
export declare function probeDialect(exec: ExecFn): Promise<RemoteDialect>;
|
|
26
|
+
/** 按连接缓存:探测键(调用方用连接身份)→ 方言 */
|
|
27
|
+
export declare class DialectCache {
|
|
28
|
+
private cache;
|
|
29
|
+
get(probeKey: string, exec: ExecFn, force?: boolean): Promise<RemoteDialect>;
|
|
30
|
+
invalidate(probeKey?: string): void;
|
|
31
|
+
}
|