Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
F
Ftt_Common
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
Ftt_Common
Commits
3ebae990
Commit
3ebae990
authored
Sep 15, 2026
by
段廷瑞
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 增加分享功能;共享知识库连接优化
parent
e9ea0397
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
554 additions
and
15 deletions
+554
-15
.gitignore
.gitignore
+2
-0
AgentTest.csproj
AgentTest/AgentTest.csproj
+1
-0
ShareHelperTests.cs
AgentTest/ShareHelperTests.cs
+28
-0
Tools.Agent.dll
AppHome/Tools.Agent.dll
+0
-0
Tools.Agent.xml
AppHome/Tools.Agent.xml
+67
-4
ShareHelper.cs
Tools.Agent/ShareHelper.cs
+32
-11
SharePackageBuilder.cs
Tools/SharePackageBuilder.cs
+251
-0
SharePackageProgressForm.cs
Tools/SharePackageProgressForm.cs
+148
-0
Tools.csproj
Tools/Tools.csproj
+4
-0
wMain.Designer.cs
Tools/wMain.Designer.cs
+9
-0
wMain.cs
Tools/wMain.cs
+12
-0
No files found.
.gitignore
View file @
3ebae990
...
@@ -53,6 +53,7 @@ bin
...
@@ -53,6 +53,7 @@ bin
/AppHome/AutoUpdate/AutoUpdate.exe
/AppHome/AutoUpdate/AutoUpdate.exe
/AppHome/config/sys_cfg.xml
/AppHome/config/sys_cfg.xml
/AppHome/Package
/AppHome/Package
/AppHome/Shares/
/AppHome/caches/default
/AppHome/caches/default
/AppHome/WebKit/caches/default
/AppHome/WebKit/caches/default
/AppHome/config/links.xml
/AppHome/config/links.xml
...
@@ -71,3 +72,4 @@ bin
...
@@ -71,3 +72,4 @@ bin
/AppHome/Agent/config/AgentConfig.json
/AppHome/Agent/config/AgentConfig.json
/cc
/cc
/AppHome/Logs
/AppHome/Logs
/AppHome/Package
AgentTest/AgentTest.csproj
View file @
3ebae990
...
@@ -71,6 +71,7 @@
...
@@ -71,6 +71,7 @@
<Compile
Include=
"Program.cs"
/>
<Compile
Include=
"Program.cs"
/>
<Compile
Include=
"TestUtil.cs"
/>
<Compile
Include=
"TestUtil.cs"
/>
<Compile
Include=
"SecretProtectorTests.cs"
/>
<Compile
Include=
"SecretProtectorTests.cs"
/>
<Compile
Include=
"ShareHelperTests.cs"
/>
<Compile
Include=
"DbQueryToolTests.cs"
/>
<Compile
Include=
"DbQueryToolTests.cs"
/>
<Compile
Include=
"AgentEngineLogicTests.cs"
/>
<Compile
Include=
"AgentEngineLogicTests.cs"
/>
<Compile
Include=
"FailoverPolicyTests.cs"
/>
<Compile
Include=
"FailoverPolicyTests.cs"
/>
...
...
AgentTest/ShareHelperTests.cs
0 → 100644
View file @
3ebae990
using
Tools.MyAgent
;
namespace
AgentTest
{
/// <summary>
/// Windows 共享连接提示测试:覆盖凭据冲突识别及设置页操作引导。
/// </summary>
public
sealed
class
ShareHelperTests
:
TestRunner
{
[
Test
]
public
void
CredentialConflictMessage_ExplainsClearCredentialsAndRetry
()
{
string
message
=
ShareHelper
.
BuildCredentialConflictMessage
(
@"\\server\share"
);
Assert
.
Contains
(
message
,
"凭据冲突"
,
"应明确说明 Windows 凭据冲突"
);
Assert
.
Contains
(
message
,
"不需要填写账号密码"
,
"应说明无需填写账号密码"
);
Assert
.
Contains
(
message
,
"清空账号和密码后重试"
,
"应给出清空凭据并重试的操作"
);
}
[
Test
]
public
void
CredentialConflict_IsRecognizedByExitCodeOrOutput
()
{
Assert
.
True
(
ShareHelper
.
IsCredentialConflict
(
1219
,
""
),
"错误码 1219 应识别为凭据冲突"
);
Assert
.
True
(
ShareHelper
.
IsCredentialConflict
(
2
,
"System error 1219"
),
"输出中的 1219 应识别为凭据冲突"
);
Assert
.
False
(
ShareHelper
.
IsCredentialConflict
(
5
,
"Access is denied"
),
"其他错误不应误判为凭据冲突"
);
}
}
}
AppHome/Tools.Agent.dll
View file @
3ebae990
No preview for this file type
AppHome/Tools.Agent.xml
View file @
3ebae990
...
@@ -13,12 +13,18 @@
...
@@ -13,12 +13,18 @@
<member
name=
"P:Tools.MyAgent.AgentRuntimeOptions.CurrentUser"
>
<member
name=
"P:Tools.MyAgent.AgentRuntimeOptions.CurrentUser"
>
<summary>
当前用户或角色,用于聊天展示、工具权限和数据库审计。
</summary>
<summary>
当前用户或角色,用于聊天展示、工具权限和数据库审计。
</summary>
</member>
</member>
<member
name=
"P:Tools.MyAgent.AgentRuntimeOptions.DiagnosticRoots"
>
<summary>
宿主注入的诊断根目录;为空时由产品上下文 ProjectRoot 自动生成 source 根。
</summary>
</member>
<member
name=
"P:Tools.MyAgent.AgentRuntimeOptions.ProductContext"
>
<member
name=
"P:Tools.MyAgent.AgentRuntimeOptions.ProductContext"
>
<summary>
宿主固定的产品上下文;空值表示未绑定产品线。
</summary>
<summary>
宿主固定的产品上下文;空值表示未绑定产品线。
</summary>
</member>
</member>
<member
name=
"T:Tools.MyAgent.AgentRuntime"
>
<member
name=
"T:Tools.MyAgent.AgentRuntime"
>
<summary>
其他 .NET Framework 项目引用 Tools.Agent 后使用的稳定入口。
</summary>
<summary>
其他 .NET Framework 项目引用 Tools.Agent 后使用的稳定入口。
</summary>
</member>
</member>
<member
name=
"P:Tools.MyAgent.AgentRuntime.DiagnosticRoots"
>
<summary>
当前运行时诊断根目录快照;返回独立副本,避免外部修改。
</summary>
</member>
<member
name=
"P:Tools.MyAgent.AgentRuntime.ProductContext"
>
<member
name=
"P:Tools.MyAgent.AgentRuntime.ProductContext"
>
<summary>
当前宿主绑定的产品上下文;返回副本,避免运行期间被外部对象修改。
</summary>
<summary>
当前宿主绑定的产品上下文;返回副本,避免运行期间被外部对象修改。
</summary>
</member>
</member>
...
@@ -162,9 +168,12 @@
...
@@ -162,9 +168,12 @@
<summary>
本轮上下文处理提示(历史省略/压缩);经 OnTextDelta 即时显示,
<summary>
本轮上下文处理提示(历史省略/压缩);经 OnTextDelta 即时显示,
并在最终回答前拼接保留(修复:此前被回答正文全量替换而丢失)
</summary>
并在最终回答前拼接保留(修复:此前被回答正文全量替换而丢失)
</summary>
</member>
</member>
<member
name=
"M:Tools.MyAgent.Agent.AgentEngine.#ctor(Tools.MyAgent.Kb.KbService,System.String,Tools.MyAgent.AgentProductContext)"
>
<member
name=
"M:Tools.MyAgent.Agent.AgentEngine.#ctor(Tools.MyAgent.Kb.KbService,System.String,Tools.MyAgent.AgentProductContext
,System.Collections.Generic.IEnumerable{Tools.MyAgent.Diagnostics.DiagnosticRoot}
)"
>
<summary>
创建引擎,并允许宿主指定内置提示词、方言、行业知识和产品上下文所在的 Agent 根目录。
</summary>
<summary>
创建引擎,并允许宿主指定内置提示词、方言、行业知识和产品上下文所在的 Agent 根目录。
</summary>
</member>
</member>
<member
name=
"P:Tools.MyAgent.Agent.AgentEngine.DiagnosticRoots"
>
<summary>
当前运行时诊断根目录快照;运行期间不可切换。
</summary>
</member>
<member
name=
"M:Tools.MyAgent.Agent.AgentEngine.SetProductContext(Tools.MyAgent.AgentProductContext)"
>
<member
name=
"M:Tools.MyAgent.Agent.AgentEngine.SetProductContext(Tools.MyAgent.AgentProductContext)"
>
<summary>
切换产品上下文。运行中的回答禁止切换,调用方应同时创建新会话。
</summary>
<summary>
切换产品上下文。运行中的回答禁止切换,调用方应同时创建新会话。
</summary>
</member>
</member>
...
@@ -456,6 +465,9 @@
...
@@ -456,6 +465,9 @@
<member
name=
"P:Tools.MyAgent.Agent.PromptBuilder.ProductContext"
>
<member
name=
"P:Tools.MyAgent.Agent.PromptBuilder.ProductContext"
>
<summary>
宿主绑定的产品上下文;由运行时注入,不接受聊天内容修改
</summary>
<summary>
宿主绑定的产品上下文;由运行时注入,不接受聊天内容修改
</summary>
</member>
</member>
<member
name=
"P:Tools.MyAgent.Agent.PromptBuilder.DiagnosticRoots"
>
<summary>
宿主注入的诊断根目录;只用于显示根目录 ID,不显示完整机器路径。
</summary>
</member>
<member
name=
"M:Tools.MyAgent.Agent.PromptBuilder.LoadLayer(System.String,System.String,System.String)"
>
<member
name=
"M:Tools.MyAgent.Agent.PromptBuilder.LoadLayer(System.String,System.String,System.String)"
>
<summary>
加载提示词模板:存在且非空 → 用文件内容;缺失 → 生成默认模板;异常 → 内置回退
</summary>
<summary>
加载提示词模板:存在且非空 → 用文件内容;缺失 → 生成默认模板;异常 → 内置回退
</summary>
</member>
</member>
...
@@ -536,6 +548,12 @@
...
@@ -536,6 +548,12 @@
<member
name=
"P:Tools.MyAgent.Agent.Tools.ToolContext.ProductContext"
>
<member
name=
"P:Tools.MyAgent.Agent.Tools.ToolContext.ProductContext"
>
<summary>
宿主绑定的产品上下文,用于限制 d_* 工具检索范围
</summary>
<summary>
宿主绑定的产品上下文,用于限制 d_* 工具检索范围
</summary>
</member>
</member>
<member
name=
"P:Tools.MyAgent.Agent.Tools.ToolContext.DiagnosticRoots"
>
<summary>
宿主注入的诊断根目录;工具只能以 rootId + 相对路径访问。
</summary>
</member>
<member
name=
"P:Tools.MyAgent.Agent.Tools.ToolContext.DiagnosticSessionId"
>
<summary>
诊断会话标识,用于将日志、工具调用和证据关联到同一次排查。
</summary>
</member>
<member
name=
"M:Tools.MyAgent.Agent.Tools.ToolContext.SafeResolve(System.Func{System.String},System.String)"
>
<member
name=
"M:Tools.MyAgent.Agent.Tools.ToolContext.SafeResolve(System.Func{System.String},System.String)"
>
<summary>
沙箱校验解析失败时回退为原路径
</summary>
<summary>
沙箱校验解析失败时回退为原路径
</summary>
</member>
</member>
...
@@ -640,6 +658,36 @@
...
@@ -640,6 +658,36 @@
<summary>
图像检索工具(#38):以参考图为模板,在知识库图片目录中模板匹配定位相似图
</summary>
<summary>
图像检索工具(#38):以参考图为模板,在知识库图片目录中模板匹配定位相似图
</summary>
</member>
</member>
<!-- Badly formed XML comment ignored for member "T:Tools.MyAgent.Agent.Tools.ModulePatchTool" -->
<!-- Badly formed XML comment ignored for member "T:Tools.MyAgent.Agent.Tools.ModulePatchTool" -->
<member
name=
"T:Tools.MyAgent.Agent.Tools.DiagnosticToolHelper"
>
<summary>
诊断工具适配器:所有路径均由 ToolContext 的诊断根目录策略解析。
</summary>
</member>
<member
name=
"T:Tools.MyAgent.Diagnostics.DiagnosticRoot"
>
<summary>
宿主注入的只读诊断根目录。
</summary>
</member>
<member
name=
"T:Tools.MyAgent.Diagnostics.DiagnosticRootPolicy"
>
<summary>
诊断根目录解析策略:只允许 rootId + 相对路径,并拒绝穿越与 reparse point。
</summary>
</member>
<member
name=
"T:Tools.MyAgent.Diagnostics.DiagnosticEvidence"
>
<summary>
进入模型前使用的诊断证据。
</summary>
</member>
<member
name=
"T:Tools.MyAgent.Diagnostics.DiagnosticResult"
>
<summary>
诊断工具统一返回结构。
</summary>
</member>
<member
name=
"T:Tools.MyAgent.Diagnostics.DiagnosticSanitizer"
>
<summary>
诊断文本脱敏,避免密码、令牌和连接串进入模型上下文。
</summary>
</member>
<member
name=
"T:Tools.MyAgent.Diagnostics.LogAnalyzer"
>
<summary>
日志发现、时间范围检索和异常上下文提取。
</summary>
</member>
<member
name=
"T:Tools.MyAgent.Diagnostics.ConfigAnalyzer"
>
<summary>
JSON/XML/INI 配置读取、脱敏和基础绑定信息提取。
</summary>
</member>
<member
name=
"T:Tools.MyAgent.Diagnostics.AssemblyInspector"
>
<summary>
只读程序集元数据检查。仅使用 ReflectionOnlyLoad,不执行目标程序集代码。
</summary>
</member>
<member
name=
"T:Tools.MyAgent.Diagnostics.DiagnosticOrchestrator"
>
<summary>
固定顺序编排日志、配置和程序集证据,避免完全依赖模型自由规划。
</summary>
</member>
<member
name=
"T:Tools.MyAgent.Config.AgentConfig"
>
<member
name=
"T:Tools.MyAgent.Config.AgentConfig"
>
<summary>
<summary>
自研智能体配置(保存于 Agent/config/AgentConfig.json)。
自研智能体配置(保存于 Agent/config/AgentConfig.json)。
...
@@ -1632,9 +1680,21 @@
...
@@ -1632,9 +1680,21 @@
<member
name=
"M:Tools.MyAgent.ShareHelper.Connect(System.String,System.String,System.String)"
>
<member
name=
"M:Tools.MyAgent.ShareHelper.Connect(System.String,System.String,System.String)"
>
<summary>
<summary>
建立到 UNC 共享的会话连接。返回 (是否成功, 描述)。
建立到 UNC 共享的会话连接。返回 (是否成功, 描述)。
<para>
通过 net use 实现;若之前已用其他凭据连接同一服务器导致凭据冲突(1219),先强制断开再重试一次。
</para>
<para>
优先使用当前 Windows 会话连接;当前身份无法访问时,且用户填写了账号,再使用显式凭据重试。
</para>
<para>
凭据冲突(1219)时返回明确引导,避免测试时静默断开现有会话。
</para>
</summary>
</summary>
</member>
</member>
<member
name=
"M:Tools.MyAgent.ShareHelper.ConnectWithFallback(System.String,System.String,System.String)"
>
<summary>
连接共享的兼容回退流程:先复用当前 Windows 会话,失败后才使用显式凭据。
</summary>
</member>
<member
name=
"M:Tools.MyAgent.ShareHelper.IsCredentialConflict(System.Int32,System.String)"
>
<summary>
判断 net use 返回结果是否为 Windows 凭据冲突(错误码 1219)。
</summary>
</member>
<member
name=
"M:Tools.MyAgent.ShareHelper.BuildCredentialConflictMessage(System.String)"
>
<summary>
生成面向设置页用户的凭据冲突处理提示。
</summary>
</member>
<member
name=
"M:Tools.MyAgent.ShareHelper.Disconnect(System.String)"
>
<member
name=
"M:Tools.MyAgent.ShareHelper.Disconnect(System.String)"
>
<summary>
断开到共享的会话连接(忽略错误)
</summary>
<summary>
断开到共享的会话连接(忽略错误)
</summary>
</member>
</member>
...
@@ -1665,10 +1725,10 @@
...
@@ -1665,10 +1725,10 @@
<member
name=
"M:Tools.MyAgent.AgentLogger.Log(System.String)"
>
<member
name=
"M:Tools.MyAgent.AgentLogger.Log(System.String)"
>
<summary>
追加一行日志(自动脱敏 + 时间戳)
</summary>
<summary>
追加一行日志(自动脱敏 + 时间戳)
</summary>
</member>
</member>
<member
name=
"M:Tools.MyAgent.AgentLogger.LogCall(System.String,System.Int32,System.Int32,System.Int64,System.String,System.String)"
>
<member
name=
"M:Tools.MyAgent.AgentLogger.LogCall(System.String,System.Int32,System.Int32,System.Int64,System.String,System.String
,System.String
)"
>
<summary>
模型调用记录(#29):model / token / 耗时 / 工具序列 / 结果
</summary>
<summary>
模型调用记录(#29):model / token / 耗时 / 工具序列 / 结果
</summary>
</member>
</member>
<member
name=
"M:Tools.MyAgent.AgentLogger.LogTool(System.String,System.String,System.Int64,System.String)"
>
<member
name=
"M:Tools.MyAgent.AgentLogger.LogTool(System.String,System.String,System.Int64,System.String
,System.String
)"
>
<summary>
工具执行记录
</summary>
<summary>
工具执行记录
</summary>
</member>
</member>
<member
name=
"M:Tools.MyAgent.AgentLogger.Mask(System.String)"
>
<member
name=
"M:Tools.MyAgent.AgentLogger.Mask(System.String)"
>
...
@@ -2228,6 +2288,9 @@
...
@@ -2228,6 +2288,9 @@
<member
name=
"M:Tools.MyAgent.wMyAgent.#ctor(System.String,System.String,Tools.MyAgent.AgentProductContext)"
>
<member
name=
"M:Tools.MyAgent.wMyAgent.#ctor(System.String,System.String,Tools.MyAgent.AgentProductContext)"
>
<summary>
创建可嵌入的智能体窗体,并允许宿主指定数据目录、当前用户和产品上下文。
</summary>
<summary>
创建可嵌入的智能体窗体,并允许宿主指定数据目录、当前用户和产品上下文。
</summary>
</member>
</member>
<member
name=
"M:Tools.MyAgent.wMyAgent.#ctor(System.String,System.String,Tools.MyAgent.AgentProductContext,System.Collections.Generic.IReadOnlyList{Tools.MyAgent.Diagnostics.DiagnosticRoot})"
>
<summary>
创建可嵌入的智能体窗体,并允许宿主指定独立诊断根目录。
</summary>
</member>
<member
name=
"M:Tools.MyAgent.wMyAgent.CreateChatControl"
>
<member
name=
"M:Tools.MyAgent.wMyAgent.CreateChatControl"
>
<summary>
<summary>
创建聊天区控件:统一使用 RichBox(RichTextBox + RTF 渲染,轻量、启动快、无浏览器依赖)。
创建聊天区控件:统一使用 RichBox(RichTextBox + RTF 渲染,轻量、启动快、无浏览器依赖)。
...
...
Tools.Agent/ShareHelper.cs
View file @
3ebae990
...
@@ -34,7 +34,8 @@ namespace Tools.MyAgent
...
@@ -34,7 +34,8 @@ namespace Tools.MyAgent
/// <summary>
/// <summary>
/// 建立到 UNC 共享的会话连接。返回 (是否成功, 描述)。
/// 建立到 UNC 共享的会话连接。返回 (是否成功, 描述)。
/// <para>通过 net use 实现;若之前已用其他凭据连接同一服务器导致凭据冲突(1219),先强制断开再重试一次。</para>
/// <para>优先使用当前 Windows 会话连接;当前身份无法访问时,且用户填写了账号,再使用显式凭据重试。</para>
/// <para>凭据冲突(1219)时返回明确引导,避免测试时静默断开现有会话。</para>
/// </summary>
/// </summary>
public
static
(
bool
Ok
,
string
Message
)
Connect
(
string
uncPath
,
string
user
,
string
password
)
public
static
(
bool
Ok
,
string
Message
)
Connect
(
string
uncPath
,
string
user
,
string
password
)
{
{
...
@@ -44,23 +45,43 @@ namespace Tools.MyAgent
...
@@ -44,23 +45,43 @@ namespace Tools.MyAgent
if
(
string
.
IsNullOrEmpty
(
share
))
if
(
string
.
IsNullOrEmpty
(
share
))
return
(
false
,
"共享路径格式不正确,应为 \\\\服务器\\共享名\\目录"
);
return
(
false
,
"共享路径格式不正确,应为 \\\\服务器\\共享名\\目录"
);
var
result
=
RunNetUse
(
BuildNetUseArgs
(
share
,
user
,
password
),
25000
);
var
result
=
ConnectWithFallback
(
share
,
user
,
password
);
if
(
result
.
Exit
==
0
)
if
(
result
.
Exit
==
0
)
return
(
true
,
$"已连接共享:
{
share
}
"
);
return
(
true
,
$"已连接共享:
{
share
}
"
);
// 凭据冲突(1219):先断开该共享的旧连接再重试一次
if
(
IsCredentialConflict
(
result
.
Exit
,
result
.
Output
))
if
(
result
.
Exit
==
ERROR_SESSION_CREDENTIAL_CONFLICT
||
return
(
false
,
BuildCredentialConflictMessage
(
share
));
result
.
Output
.
IndexOf
(
"1219"
,
StringComparison
.
Ordinal
)
>=
0
)
{
RunNetUse
(
$"\"
{
share
}
\" /delete /y"
,
10000
);
result
=
RunNetUse
(
BuildNetUseArgs
(
share
,
user
,
password
),
25000
);
if
(
result
.
Exit
==
0
)
return
(
true
,
$"已连接共享:
{
share
}
"
);
}
return
(
false
,
BuildFailureMessage
(
result
,
share
));
return
(
false
,
BuildFailureMessage
(
result
,
share
));
}
}
/// <summary>
/// 连接共享的兼容回退流程:先复用当前 Windows 会话,失败后才使用显式凭据。
/// </summary>
private
static
(
int
Exit
,
string
Output
)
ConnectWithFallback
(
string
share
,
string
user
,
string
password
)
{
var
currentSession
=
RunNetUse
(
BuildNetUseArgs
(
share
,
""
,
""
),
25000
);
if
(
currentSession
.
Exit
==
0
||
string
.
IsNullOrWhiteSpace
(
user
))
return
currentSession
;
return
RunNetUse
(
BuildNetUseArgs
(
share
,
user
,
password
),
25000
);
}
/// <summary>判断 net use 返回结果是否为 Windows 凭据冲突(错误码 1219)。</summary>
internal
static
bool
IsCredentialConflict
(
int
exitCode
,
string
output
)
{
return
exitCode
==
ERROR_SESSION_CREDENTIAL_CONFLICT
||
(
output
??
""
).
IndexOf
(
"1219"
,
StringComparison
.
Ordinal
)
>=
0
;
}
/// <summary>生成面向设置页用户的凭据冲突处理提示。</summary>
internal
static
string
BuildCredentialConflictMessage
(
string
share
)
{
return
$"连接共享失败(
{
share
}
):检测到 Windows 凭据冲突(错误 1219)。"
+
"当前 Windows 会话已使用其他账号连接此服务器;测试连接不需要填写账号密码,请清空账号和密码后重试。"
+
"若仍失败,请先断开该服务器已有网络连接,再重试。"
;
}
/// <summary>断开到共享的会话连接(忽略错误)</summary>
/// <summary>断开到共享的会话连接(忽略错误)</summary>
public
static
void
Disconnect
(
string
uncPath
)
public
static
void
Disconnect
(
string
uncPath
)
{
{
...
...
Tools/SharePackageBuilder.cs
0 → 100644
View file @
3ebae990
using
Newtonsoft.Json
;
using
System
;
using
System.Collections.Generic
;
using
System.IO
;
using
System.IO.Compression
;
using
System.Linq
;
using
System.Text
;
using
Tools.MyUtils
;
namespace
Tools
{
/// <summary>
/// 生成可分享的 Tools 运行目录压缩包。
/// <para>保留程序文件和公共资源,排除本机连接、登录信息、Agent 凭据、历史、索引、日志及缓存;
/// 仅额外保留脱敏后的更新版本标识。</para>
/// </summary>
internal
static
class
SharePackageBuilder
{
private
static
readonly
HashSet
<
string
>
ExcludedRootDirectories
=
new
HashSet
<
string
>(
StringComparer
.
OrdinalIgnoreCase
)
{
".vs"
,
"Logs"
,
"log"
,
"temp"
,
"save"
,
"Package"
,
"Shares"
,
"GPUCache"
,
"ClaudeChat"
,
"AgentChat"
,
"TestResults"
,
"caches"
,
};
private
static
readonly
HashSet
<
string
>
PublicAgentDirectories
=
new
HashSet
<
string
>(
StringComparer
.
OrdinalIgnoreCase
)
{
"prompts"
,
"dialect"
,
"industry"
,
};
private
static
readonly
HashSet
<
string
>
PrivateConfigFiles
=
new
HashSet
<
string
>(
StringComparer
.
OrdinalIgnoreCase
)
{
"LoginMemory.json"
,
"db.json"
,
"links.xml"
,
"project.json"
,
"sys_cfg.xml"
,
"DetectedLogPaths.json"
,
"List`1.json"
,
};
private
static
readonly
HashSet
<
string
>
PublicConfigFiles
=
new
HashSet
<
string
>(
StringComparer
.
OrdinalIgnoreCase
)
{
"模板.xlsx"
,
};
public
static
SharePackageResult
Create
(
string
sourceRoot
,
string
outputDirectory
)
{
return
Create
(
sourceRoot
,
outputDirectory
,
null
);
}
public
static
SharePackageResult
Create
(
string
sourceRoot
,
string
outputDirectory
,
IProgress
<
SharePackageProgress
>
progress
)
{
if
(
string
.
IsNullOrWhiteSpace
(
sourceRoot
))
throw
new
ArgumentException
(
"分享源目录不能为空。"
,
nameof
(
sourceRoot
));
string
source
=
NormalizeRoot
(
sourceRoot
);
if
(!
Directory
.
Exists
(
source
))
throw
new
DirectoryNotFoundException
(
"分享源目录不存在:"
+
source
);
string
output
=
Path
.
GetFullPath
(
outputDirectory
??
""
);
Directory
.
CreateDirectory
(
output
);
string
fileName
=
"ToolsShare-"
+
DateTime
.
Now
.
ToString
(
"yyyyMMddHHmmssfff"
)
+
".zip"
;
string
archivePath
=
Path
.
Combine
(
output
,
fileName
);
string
tempPath
=
archivePath
+
".tmp"
;
int
fileCount
=
0
;
try
{
var
files
=
EnumerateShareFiles
(
source
).
ToList
();
string
versionMarker
=
ReadVersionMarker
(
source
);
int
total
=
files
.
Count
+
(
versionMarker
==
null
?
0
:
1
);
progress
?.
Report
(
new
SharePackageProgress
(
0
,
total
,
"正在分析可分享文件..."
));
if
(
File
.
Exists
(
tempPath
))
File
.
Delete
(
tempPath
);
using
(
var
archive
=
ZipFile
.
Open
(
tempPath
,
ZipArchiveMode
.
Create
))
{
foreach
(
var
file
in
files
)
{
archive
.
CreateEntryFromFile
(
file
.
FullName
,
file
.
RelativePath
,
CompressionLevel
.
Fastest
);
fileCount
++;
progress
?.
Report
(
new
SharePackageProgress
(
fileCount
,
total
,
"正在打包:"
+
file
.
RelativePath
));
}
if
(
versionMarker
!=
null
)
{
WriteVersionMarker
(
archive
,
versionMarker
);
fileCount
++;
progress
?.
Report
(
new
SharePackageProgress
(
fileCount
,
total
,
"正在写入脱敏版本标识..."
));
}
}
File
.
Move
(
tempPath
,
archivePath
);
return
new
SharePackageResult
(
archivePath
,
fileCount
,
new
FileInfo
(
archivePath
).
Length
);
}
catch
{
try
{
if
(
File
.
Exists
(
tempPath
))
File
.
Delete
(
tempPath
);
}
catch
{
}
try
{
if
(
File
.
Exists
(
archivePath
))
File
.
Delete
(
archivePath
);
}
catch
{
}
throw
;
}
}
private
static
IEnumerable
<
ShareFile
>
EnumerateShareFiles
(
string
sourceRoot
)
{
var
pending
=
new
Stack
<
string
>();
pending
.
Push
(
sourceRoot
);
while
(
pending
.
Count
>
0
)
{
string
current
=
pending
.
Pop
();
foreach
(
string
directory
in
Directory
.
GetDirectories
(
current
))
{
if
(
IsReparsePoint
(
directory
))
continue
;
string
relative
=
ToRelative
(
sourceRoot
,
directory
);
if
(!
ShouldExclude
(
relative
,
true
))
pending
.
Push
(
directory
);
}
foreach
(
string
file
in
Directory
.
GetFiles
(
current
))
{
if
(
IsReparsePoint
(
file
))
continue
;
string
relative
=
ToRelative
(
sourceRoot
,
file
);
if
(!
ShouldExclude
(
relative
,
false
))
yield
return
new
ShareFile
(
file
,
relative
);
}
}
}
private
static
bool
ShouldExclude
(
string
relativePath
,
bool
isDirectory
)
{
string
path
=
(
relativePath
??
""
).
Replace
(
'/'
,
'\\'
).
TrimStart
(
'\\'
);
if
(
path
.
Length
==
0
)
return
false
;
string
[]
parts
=
path
.
Split
(
new
[]
{
'\\'
},
StringSplitOptions
.
RemoveEmptyEntries
);
if
(
parts
.
Length
>
0
&&
ExcludedRootDirectories
.
Contains
(
parts
[
0
]))
return
true
;
if
(
path
.
Equals
(
"config"
,
StringComparison
.
OrdinalIgnoreCase
))
return
false
;
if
(
path
.
StartsWith
(
"config\\help\\"
,
StringComparison
.
OrdinalIgnoreCase
)
||
path
.
Equals
(
"config\\help"
,
StringComparison
.
OrdinalIgnoreCase
)
||
path
.
StartsWith
(
"config\\InitSql\\"
,
StringComparison
.
OrdinalIgnoreCase
)
||
path
.
Equals
(
"config\\InitSql"
,
StringComparison
.
OrdinalIgnoreCase
))
return
false
;
if
(
path
.
StartsWith
(
"config\\"
,
StringComparison
.
OrdinalIgnoreCase
))
{
string
fileName
=
Path
.
GetFileName
(
path
);
return
!
isDirectory
&&
(
PrivateConfigFiles
.
Contains
(
fileName
)
||
!
PublicConfigFiles
.
Contains
(
fileName
));
}
if
(
path
.
Equals
(
"Agent"
,
StringComparison
.
OrdinalIgnoreCase
))
return
false
;
if
(
path
.
StartsWith
(
"Agent\\"
,
StringComparison
.
OrdinalIgnoreCase
))
{
string
[]
agentParts
=
path
.
Split
(
new
[]
{
'\\'
},
StringSplitOptions
.
RemoveEmptyEntries
);
return
agentParts
.
Length
==
0
||
!
PublicAgentDirectories
.
Contains
(
agentParts
[
1
]);
}
string
extension
=
Path
.
GetExtension
(
path
);
if
(
extension
.
Equals
(
".log"
,
StringComparison
.
OrdinalIgnoreCase
)
||
extension
.
Equals
(
".pdb"
,
StringComparison
.
OrdinalIgnoreCase
)
||
extension
.
Equals
(
".zip"
,
StringComparison
.
OrdinalIgnoreCase
)
||
extension
.
Equals
(
".user"
,
StringComparison
.
OrdinalIgnoreCase
)
||
extension
.
Equals
(
".suo"
,
StringComparison
.
OrdinalIgnoreCase
)
||
extension
.
Equals
(
".tmp"
,
StringComparison
.
OrdinalIgnoreCase
)
||
extension
.
Equals
(
".db"
,
StringComparison
.
OrdinalIgnoreCase
))
return
true
;
return
false
;
}
private
static
string
ReadVersionMarker
(
string
sourceRoot
)
{
string
path
=
Path
.
Combine
(
sourceRoot
,
"config"
,
"LoginMemory.json"
);
if
(!
File
.
Exists
(
path
))
return
null
;
try
{
var
memory
=
JsonConvert
.
DeserializeObject
<
LoginMemory
>(
File
.
ReadAllText
(
path
,
Encoding
.
UTF8
));
if
(
memory
==
null
)
return
null
;
return
JsonConvert
.
SerializeObject
(
new
{
UpdateTime
=
memory
.
UpdateTime
??
""
,
},
Formatting
.
Indented
);
}
catch
(
Exception
ex
)
{
throw
new
InvalidDataException
(
"读取更新版本标识失败:"
+
ex
.
Message
,
ex
);
}
}
private
static
void
WriteVersionMarker
(
ZipArchive
archive
,
string
content
)
{
var
entry
=
archive
.
CreateEntry
(
"config/LoginMemory.json"
);
using
(
var
stream
=
entry
.
Open
())
using
(
var
writer
=
new
StreamWriter
(
stream
,
new
UTF8Encoding
(
false
)))
writer
.
Write
(
content
);
}
private
static
string
NormalizeRoot
(
string
path
)
{
return
Path
.
GetFullPath
(
path
).
TrimEnd
(
Path
.
DirectorySeparatorChar
,
Path
.
AltDirectorySeparatorChar
);
}
private
static
string
ToRelative
(
string
root
,
string
fullPath
)
{
return
fullPath
.
Substring
(
root
.
Length
).
TrimStart
(
'\\'
,
'/'
).
Replace
(
'\\'
,
'/'
);
}
private
static
bool
IsReparsePoint
(
string
path
)
{
try
{
return
(
File
.
GetAttributes
(
path
)
&
FileAttributes
.
ReparsePoint
)
!=
0
;
}
catch
{
return
true
;
}
}
private
sealed
class
ShareFile
{
public
ShareFile
(
string
fullName
,
string
relativePath
)
{
FullName
=
fullName
;
RelativePath
=
relativePath
;
}
public
string
FullName
{
get
;
}
public
string
RelativePath
{
get
;
}
}
}
internal
sealed
class
SharePackageProgress
{
public
SharePackageProgress
(
int
current
,
int
total
,
string
status
)
{
Current
=
current
;
Total
=
total
;
Status
=
status
??
""
;
}
public
int
Current
{
get
;
}
public
int
Total
{
get
;
}
public
string
Status
{
get
;
}
}
internal
sealed
class
SharePackageResult
{
public
SharePackageResult
(
string
archivePath
,
int
fileCount
,
long
length
)
{
ArchivePath
=
archivePath
;
FileCount
=
fileCount
;
Length
=
length
;
}
public
string
ArchivePath
{
get
;
}
public
int
FileCount
{
get
;
}
public
long
Length
{
get
;
}
}
}
Tools/SharePackageProgressForm.cs
0 → 100644
View file @
3ebae990
using
System
;
using
System.Diagnostics
;
using
System.Drawing
;
using
System.Threading.Tasks
;
using
System.Windows.Forms
;
namespace
Tools
{
/// <summary>
/// 分享打包进度窗口:显示文件处理进度,完成后可直接打开 Shares 目录。
/// </summary>
internal
sealed
class
SharePackageProgressForm
:
Form
{
private
readonly
string
_sourceRoot
;
private
readonly
string
_outputDirectory
;
private
ProgressBar
_progressBar
;
private
Label
_statusLabel
;
private
Button
_openFolderButton
;
private
Button
_closeButton
;
private
bool
_started
;
private
bool
_finished
;
public
SharePackageProgressForm
(
string
sourceRoot
,
string
outputDirectory
)
{
_sourceRoot
=
sourceRoot
;
_outputDirectory
=
outputDirectory
;
InitializeControls
();
Shown
+=
StartBuild
;
}
private
void
InitializeControls
()
{
Text
=
"分享打包"
;
StartPosition
=
FormStartPosition
.
CenterParent
;
FormBorderStyle
=
FormBorderStyle
.
FixedDialog
;
MinimizeBox
=
false
;
MaximizeBox
=
false
;
ShowInTaskbar
=
false
;
ClientSize
=
new
Size
(
520
,
174
);
var
titleLabel
=
new
Label
{
AutoSize
=
true
,
Location
=
new
Point
(
20
,
16
),
Text
=
"正在生成分享包,请稍候..."
,
};
_statusLabel
=
new
Label
{
AutoEllipsis
=
true
,
Location
=
new
Point
(
20
,
43
),
Size
=
new
Size
(
480
,
22
),
Text
=
"准备中..."
,
};
_progressBar
=
new
ProgressBar
{
Location
=
new
Point
(
20
,
72
),
Size
=
new
Size
(
480
,
24
),
Minimum
=
0
,
Maximum
=
1
,
Value
=
0
,
Style
=
ProgressBarStyle
.
Blocks
,
};
_openFolderButton
=
new
Button
{
Location
=
new
Point
(
290
,
122
),
Size
=
new
Size
(
104
,
30
),
Text
=
"打开文件夹"
,
Enabled
=
false
,
};
_openFolderButton
.
Click
+=
OpenFolder
;
_closeButton
=
new
Button
{
Location
=
new
Point
(
406
,
122
),
Size
=
new
Size
(
94
,
30
),
Text
=
"关闭"
,
Enabled
=
false
,
};
_closeButton
.
Click
+=
(
sender
,
e
)
=>
Close
();
Controls
.
Add
(
titleLabel
);
Controls
.
Add
(
_statusLabel
);
Controls
.
Add
(
_progressBar
);
Controls
.
Add
(
_openFolderButton
);
Controls
.
Add
(
_closeButton
);
}
private
async
void
StartBuild
(
object
sender
,
EventArgs
e
)
{
if
(
_started
)
return
;
_started
=
true
;
try
{
var
progress
=
new
Progress
<
SharePackageProgress
>(
UpdateProgress
);
var
result
=
await
Task
.
Run
(()
=>
SharePackageBuilder
.
Create
(
_sourceRoot
,
_outputDirectory
,
progress
));
_finished
=
true
;
_statusLabel
.
Text
=
$"打包完成:
{
result
.
FileCount
}
个文件,
{
result
.
Length
/
1024d
/
1024d
:
0.00
}
MB"
;
_openFolderButton
.
Enabled
=
true
;
_closeButton
.
Enabled
=
true
;
}
catch
(
Exception
ex
)
{
_finished
=
true
;
_statusLabel
.
Text
=
"打包失败:"
+
ex
.
Message
;
_closeButton
.
Enabled
=
true
;
}
}
private
void
UpdateProgress
(
SharePackageProgress
progress
)
{
if
(
IsDisposed
||
progress
==
null
)
return
;
int
total
=
Math
.
Max
(
progress
.
Total
,
1
);
_progressBar
.
Maximum
=
total
;
_progressBar
.
Value
=
Math
.
Min
(
Math
.
Max
(
progress
.
Current
,
0
),
total
);
_statusLabel
.
Text
=
progress
.
Total
>
0
?
$"
{
progress
.
Current
}
/
{
progress
.
Total
}
{
progress
.
Status
}
"
:
progress
.
Status
;
}
private
void
OpenFolder
(
object
sender
,
EventArgs
e
)
{
try
{
Process
.
Start
(
new
ProcessStartInfo
{
FileName
=
_outputDirectory
,
UseShellExecute
=
true
,
});
}
catch
(
Exception
ex
)
{
MessageBox
.
Show
(
this
,
"打开分享目录失败:\r\n"
+
ex
.
Message
,
"分享打包"
,
MessageBoxButtons
.
OK
,
MessageBoxIcon
.
Error
);
}
}
protected
override
void
OnFormClosing
(
FormClosingEventArgs
e
)
{
if
(!
_finished
)
{
e
.
Cancel
=
true
;
return
;
}
base
.
OnFormClosing
(
e
);
}
}
}
Tools/Tools.csproj
View file @
3ebae990
...
@@ -424,6 +424,10 @@
...
@@ -424,6 +424,10 @@
<DependentUpon>
wXmlGenerator.cs
</DependentUpon>
<DependentUpon>
wXmlGenerator.cs
</DependentUpon>
</Compile>
</Compile>
<Compile
Include=
"wExtend.cs"
/>
<Compile
Include=
"wExtend.cs"
/>
<Compile
Include=
"SharePackageBuilder.cs"
/>
<Compile
Include=
"SharePackageProgressForm.cs"
>
<SubType>
Form
</SubType>
</Compile>
<Compile
Include=
"wMain.cs"
>
<Compile
Include=
"wMain.cs"
>
<SubType>
Form
</SubType>
<SubType>
Form
</SubType>
</Compile>
</Compile>
...
...
Tools/wMain.Designer.cs
View file @
3ebae990
...
@@ -42,6 +42,7 @@
...
@@ -42,6 +42,7 @@
this
.
tsmiSysUpload
=
new
System
.
Windows
.
Forms
.
ToolStripMenuItem
();
this
.
tsmiSysUpload
=
new
System
.
Windows
.
Forms
.
ToolStripMenuItem
();
this
.
tsmiSysUpdate
=
new
System
.
Windows
.
Forms
.
ToolStripMenuItem
();
this
.
tsmiSysUpdate
=
new
System
.
Windows
.
Forms
.
ToolStripMenuItem
();
this
.
tsmiSysUpdateLog
=
new
System
.
Windows
.
Forms
.
ToolStripMenuItem
();
this
.
tsmiSysUpdateLog
=
new
System
.
Windows
.
Forms
.
ToolStripMenuItem
();
this
.
tsmiSysShare
=
new
System
.
Windows
.
Forms
.
ToolStripMenuItem
();
this
.
tsmiSysExit
=
new
System
.
Windows
.
Forms
.
ToolStripMenuItem
();
this
.
tsmiSysExit
=
new
System
.
Windows
.
Forms
.
ToolStripMenuItem
();
this
.
toolStripMenuItem2
=
new
System
.
Windows
.
Forms
.
ToolStripMenuItem
();
this
.
toolStripMenuItem2
=
new
System
.
Windows
.
Forms
.
ToolStripMenuItem
();
this
.
tsmiAccPasswordMgr
=
new
System
.
Windows
.
Forms
.
ToolStripMenuItem
();
this
.
tsmiAccPasswordMgr
=
new
System
.
Windows
.
Forms
.
ToolStripMenuItem
();
...
@@ -199,6 +200,7 @@
...
@@ -199,6 +200,7 @@
this
.
tsmiSysUpload
,
this
.
tsmiSysUpload
,
this
.
tsmiSysUpdate
,
this
.
tsmiSysUpdate
,
this
.
tsmiSysUpdateLog
,
this
.
tsmiSysUpdateLog
,
this
.
tsmiSysShare
,
this
.
tsmiSysExit
});
this
.
tsmiSysExit
});
this
.
toolStripMenuItem1
.
Name
=
"toolStripMenuItem1"
;
this
.
toolStripMenuItem1
.
Name
=
"toolStripMenuItem1"
;
this
.
toolStripMenuItem1
.
Size
=
new
System
.
Drawing
.
Size
(
44
,
21
);
this
.
toolStripMenuItem1
.
Size
=
new
System
.
Drawing
.
Size
(
44
,
21
);
...
@@ -221,6 +223,12 @@
...
@@ -221,6 +223,12 @@
this
.
tsmiSysUpdateLog
.
Name
=
"tsmiSysUpdateLog"
;
this
.
tsmiSysUpdateLog
.
Name
=
"tsmiSysUpdateLog"
;
this
.
tsmiSysUpdateLog
.
Size
=
new
System
.
Drawing
.
Size
(
124
,
22
);
this
.
tsmiSysUpdateLog
.
Size
=
new
System
.
Drawing
.
Size
(
124
,
22
);
this
.
tsmiSysUpdateLog
.
Text
=
"更新记录"
;
this
.
tsmiSysUpdateLog
.
Text
=
"更新记录"
;
//
// tsmiSysShare
//
this
.
tsmiSysShare
.
Name
=
"tsmiSysShare"
;
this
.
tsmiSysShare
.
Size
=
new
System
.
Drawing
.
Size
(
124
,
22
);
this
.
tsmiSysShare
.
Text
=
"分享打包"
;
//
//
// tsmiSysExit
// tsmiSysExit
//
//
...
@@ -551,6 +559,7 @@
...
@@ -551,6 +559,7 @@
private
System
.
Windows
.
Forms
.
Label
label3
;
private
System
.
Windows
.
Forms
.
Label
label3
;
private
System
.
Windows
.
Forms
.
Timer
timer1
;
private
System
.
Windows
.
Forms
.
Timer
timer1
;
private
System
.
Windows
.
Forms
.
ToolStripMenuItem
tsmiSysUpdateLog
;
private
System
.
Windows
.
Forms
.
ToolStripMenuItem
tsmiSysUpdateLog
;
private
System
.
Windows
.
Forms
.
ToolStripMenuItem
tsmiSysShare
;
private
Tools
.
MyCommon
.
controls
.
TabControlEx
tabControl1
;
private
Tools
.
MyCommon
.
controls
.
TabControlEx
tabControl1
;
private
System
.
Windows
.
Forms
.
TabPage
HomePage
;
private
System
.
Windows
.
Forms
.
TabPage
HomePage
;
private
System
.
Windows
.
Forms
.
ToolStripMenuItem
tsmiShowFetetoWork
;
private
System
.
Windows
.
Forms
.
ToolStripMenuItem
tsmiShowFetetoWork
;
...
...
Tools/wMain.cs
View file @
3ebae990
...
@@ -376,6 +376,7 @@ namespace Tools
...
@@ -376,6 +376,7 @@ namespace Tools
case
"SysUpload"
:
this
.
UploadUpdate
();
break
;
case
"SysUpload"
:
this
.
UploadUpdate
();
break
;
case
"SysUpdate"
:
this
.
CheckUpdate
();
break
;
case
"SysUpdate"
:
this
.
CheckUpdate
();
break
;
case
"SysUpdateLog"
:
this
.
ViewUpdateLog
();
break
;
case
"SysUpdateLog"
:
this
.
ViewUpdateLog
();
break
;
case
"SysShare"
:
this
.
SharePackage
();
break
;
case
"SysExit"
:
this
.
Exit
();
break
;
case
"SysExit"
:
this
.
Exit
();
break
;
case
"AccPasswordMgr"
:
this
.
PasswordMgr
();
break
;
case
"AccPasswordMgr"
:
this
.
PasswordMgr
();
break
;
case
"AccPersonalSetting"
:
this
.
PersonalSetting
();
break
;
case
"AccPersonalSetting"
:
this
.
PersonalSetting
();
break
;
...
@@ -465,6 +466,17 @@ namespace Tools
...
@@ -465,6 +466,17 @@ namespace Tools
//
//
Utils
.
Tip
(
sb
.
ToString
(),
600
,
400
);
Utils
.
Tip
(
sb
.
ToString
(),
600
,
400
);
}
}
/// <summary>
/// 系统:生成分享包
/// </summary>
private
void
SharePackage
()
{
string
root
=
AppDomain
.
CurrentDomain
.
BaseDirectory
;
string
output
=
Path
.
Combine
(
root
,
"Shares"
);
using
(
var
progress
=
new
SharePackageProgressForm
(
root
,
output
))
progress
.
ShowDialog
(
this
);
}
/// <summary>
/// <summary>
/// 系统:退出系统
/// 系统:退出系统
/// </summary>
/// </summary>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment