@foggy-projects/deepseek-harness-plugin 0.4.0-beta.9 → 0.4.0-rc.2
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/README.md +55 -20
- package/docs/PUBLIC-BETA-READINESS.md +71 -15
- package/docs/RELEASE-CANDIDATE-0.4.0-RC.2.md +38 -0
- package/docs/WINDOWS-BETA-ACCEPTANCE.md +57 -22
- package/experience/linux/prepare.sh +2 -2
- package/lib/client.js +249 -27
- package/lib/diagnostics.js +86 -0
- package/lib/index.js +113 -15
- package/lib/remote-descriptor.js +16 -2
- package/lib/runtime-settings.js +89 -0
- package/lib/skill-provider.js +13 -8
- package/package.json +11 -10
- package/skills/foggy-deepseek-onboarding/SKILL.md +126 -127
- package/skills/foggy-deepseek-onboarding/assets/connection.schema.json +6 -8
- package/skills/foggy-deepseek-onboarding/assets/datasource.example.json +3 -2
- package/skills/foggy-deepseek-onboarding/assets/env.example +4 -4
- package/skills/foggy-deepseek-onboarding/assets/onboarding-state.schema.json +30 -1
- package/skills/foggy-deepseek-onboarding/assets/versions.json +54 -29
- package/skills/foggy-deepseek-onboarding/references/onboarding-workflow.md +134 -122
- package/skills/foggy-deepseek-onboarding/scripts/invoke-onboarding.ps1 +2 -0
- package/skills/foggy-deepseek-onboarding/scripts/onboarding.py +689 -81
package/lib/client.js
CHANGED
|
@@ -8,25 +8,47 @@ window.__ModuleLoader__.load({
|
|
|
8
8
|
|
|
9
9
|
const NS = 'settings.foggy'
|
|
10
10
|
const passthroughSchema = { parse: (value) => value }
|
|
11
|
+
const runtimeSettingsInputSchema = {
|
|
12
|
+
parse: (value) => {
|
|
13
|
+
const port = value?.port
|
|
14
|
+
if (!Number.isInteger(port) || port < 1024 || port > 65535 || Object.keys(value || {}).some((key) => key !== 'port')) {
|
|
15
|
+
throw new Error('Runtime port must be an integer between 1024 and 65535')
|
|
16
|
+
}
|
|
17
|
+
return { port }
|
|
18
|
+
},
|
|
19
|
+
}
|
|
20
|
+
const descriptor = (method, parameters = []) => ({
|
|
21
|
+
id: `@foggy-projects/deepseek-harness-plugin#foggyIntegration/${method}`,
|
|
22
|
+
service: 'foggyIntegration',
|
|
23
|
+
namespace: 'foggyIntegration',
|
|
24
|
+
method,
|
|
25
|
+
invocation: { kind: 'direct' },
|
|
26
|
+
parameters,
|
|
27
|
+
result: {
|
|
28
|
+
mode: 'strict',
|
|
29
|
+
typeSymbol: '@foggy-projects/deepseek-harness-plugin#FoggyIntegrationResult',
|
|
30
|
+
schema: passthroughSchema,
|
|
31
|
+
},
|
|
32
|
+
sourceLocation: { file: 'lib/index.js', line: 1, column: 1 },
|
|
33
|
+
})
|
|
11
34
|
const TYPERT_REMOTE = {
|
|
12
35
|
package: '@foggy-projects/deepseek-harness-plugin',
|
|
13
36
|
descriptors: [
|
|
14
|
-
|
|
37
|
+
...[
|
|
38
|
+
'status', 'plan', 'initialize', 'repair', 'repairPython', 'repairCli', 'repairLauncher', 'repairAnalysisSkill', 'repairSemanticQuerySkill',
|
|
15
39
|
'migrateProfiles', 'diagnostics', 'runtimeStart', 'runtimeStop',
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
sourceLocation: { file: 'lib/index.js', line: 1, column: 1 },
|
|
29
|
-
})),
|
|
40
|
+
].map((method) => descriptor(method)),
|
|
41
|
+
descriptor('saveRuntimeSettings', [{
|
|
42
|
+
name: 'input',
|
|
43
|
+
wire: 'input',
|
|
44
|
+
source: 'json',
|
|
45
|
+
codec: {
|
|
46
|
+
mode: 'strict',
|
|
47
|
+
typeSymbol: '@foggy-projects/deepseek-harness-plugin#FoggyRuntimeSettingsInput',
|
|
48
|
+
schema: runtimeSettingsInputSchema,
|
|
49
|
+
},
|
|
50
|
+
}]),
|
|
51
|
+
],
|
|
30
52
|
}
|
|
31
53
|
|
|
32
54
|
const zh = {
|
|
@@ -44,6 +66,7 @@ window.__ModuleLoader__.load({
|
|
|
44
66
|
cli: 'CLI',
|
|
45
67
|
launcher: 'Launcher',
|
|
46
68
|
analysisSkill: '分析 Skill',
|
|
69
|
+
semanticQuerySkill: '语义查询 Skill',
|
|
47
70
|
onboardingSkill: '引导 Skill',
|
|
48
71
|
skillRegistry: 'DSH Skill 注册',
|
|
49
72
|
python: 'Python',
|
|
@@ -59,6 +82,7 @@ window.__ModuleLoader__.load({
|
|
|
59
82
|
repairCli: '检查 / 修复 CLI',
|
|
60
83
|
repairLauncher: '检查 / 修复 Launcher',
|
|
61
84
|
repairAnalysisSkill: '检查 / 修复分析 Skill',
|
|
85
|
+
repairSemanticQuerySkill: '检查 / 修复语义查询 Skill',
|
|
62
86
|
diagnostics: '导出诊断报告',
|
|
63
87
|
diagnosticsSaved: '诊断报告已保存',
|
|
64
88
|
start: '启动 Runtime',
|
|
@@ -70,10 +94,20 @@ window.__ModuleLoader__.load({
|
|
|
70
94
|
progressCli: '准备 CLI',
|
|
71
95
|
progressLauncher: '准备 Launcher',
|
|
72
96
|
progressAnalysis: '准备分析 Skill',
|
|
97
|
+
progressSemanticQuery: '准备语义查询 Skill',
|
|
73
98
|
progressSkills: '注册 DSH Skills',
|
|
74
99
|
progressState: '写入安装状态',
|
|
75
100
|
progressComplete: '初始化完成',
|
|
101
|
+
progressRuntimePreflight: '检查 Runtime 状态',
|
|
102
|
+
progressRuntimeLauncher: '启动 Java Launcher',
|
|
103
|
+
progressRuntimeReadiness: '等待 Runtime 就绪',
|
|
104
|
+
progressRuntimeCapabilities: '验证 Runtime 能力',
|
|
105
|
+
progressRuntimeState: '保存 Runtime 状态',
|
|
106
|
+
progressRuntimeComplete: 'Runtime 已就绪',
|
|
76
107
|
progressFiles: '文件',
|
|
108
|
+
progressElapsed: '已等待',
|
|
109
|
+
progressTimeout: '最长',
|
|
110
|
+
progressSeconds: '秒',
|
|
77
111
|
managedPrivate: 'Foggy 私有',
|
|
78
112
|
configuredOverride: '外部指定',
|
|
79
113
|
accepted: '操作已开始;页面会自动刷新状态。',
|
|
@@ -90,6 +124,10 @@ window.__ModuleLoader__.load({
|
|
|
90
124
|
noOnboarding: '尚无数据库引导记录;请在对话中调用 Foggy onboarding Skill。',
|
|
91
125
|
projectRoot: '工作区',
|
|
92
126
|
resumeInChat: '未完成步骤应回到对应工作区,在对话中继续。',
|
|
127
|
+
queryWarningTitle: '查询输入已自动兼容',
|
|
128
|
+
queryWarningSummary: 'Runtime 在 WARN 模式下忽略了未知的查询属性,查询仍已执行。请检查告警代码与证据后决定是否修正模型或请求。',
|
|
129
|
+
queryWarningCodes: '告警代码',
|
|
130
|
+
queryWarningEvidence: '证据目录',
|
|
93
131
|
stepPlanned: '规划',
|
|
94
132
|
stepDatasourceConfigured: '数据库连接',
|
|
95
133
|
stepDatasourceVerified: '连接验证',
|
|
@@ -99,6 +137,21 @@ window.__ModuleLoader__.load({
|
|
|
99
137
|
stepSemanticPublished: '发布',
|
|
100
138
|
stepSemanticVerified: '首次查询',
|
|
101
139
|
runtimeUrl: 'Runtime 地址',
|
|
140
|
+
runtimeSettingsTitle: 'Runtime 连接设置',
|
|
141
|
+
runtimePort: 'Runtime 端口',
|
|
142
|
+
runtimePortHint: '端口会持久保存,CLI 与 Skill 将使用同一个 Runtime 地址。',
|
|
143
|
+
configuredRuntimeUrl: '下次启动地址',
|
|
144
|
+
saveRuntimePort: '保存端口',
|
|
145
|
+
runtimePortSaved: 'Runtime 端口已保存',
|
|
146
|
+
stopToChangePort: 'Runtime 运行时不能修改端口,请先停止 Runtime。',
|
|
147
|
+
errorTitle: 'Foggy 操作失败',
|
|
148
|
+
portUnavailableTitle: 'Runtime 端口被占用',
|
|
149
|
+
portUnavailableMessage: '已被其他程序或 Windows 端口代理占用,Runtime 无法启动。',
|
|
150
|
+
portUnavailableHelp: '请更换端口后保存并重新启动;也可以先释放占用该端口的程序或 Windows 端口代理。',
|
|
151
|
+
portInvalidTitle: '端口配置无效',
|
|
152
|
+
portInvalidMessage: '当前值无法保存为 Runtime 端口。',
|
|
153
|
+
portInvalidHelp: '请输入 1024 到 65535 之间的整数端口。',
|
|
154
|
+
settingsInvalidTitle: 'Runtime 设置文件异常',
|
|
102
155
|
nextTitle: '后续配置',
|
|
103
156
|
nextCopy: 'Skills 已通过 DeepSeek Harness 原生注册表提供给所有工作区;每个会话直接使用自己的工作目录。Runtime 启动成功后,将继续进入数据库连接与语义层向导。',
|
|
104
157
|
beta: 'Beta',
|
|
@@ -119,6 +172,7 @@ window.__ModuleLoader__.load({
|
|
|
119
172
|
cli: 'CLI',
|
|
120
173
|
launcher: 'Launcher',
|
|
121
174
|
analysisSkill: 'Analysis Skill',
|
|
175
|
+
semanticQuerySkill: 'Semantic Query Skill',
|
|
122
176
|
onboardingSkill: 'Onboarding Skill',
|
|
123
177
|
skillRegistry: 'DSH Skill registry',
|
|
124
178
|
python: 'Python',
|
|
@@ -134,6 +188,7 @@ window.__ModuleLoader__.load({
|
|
|
134
188
|
repairCli: 'Check / repair CLI',
|
|
135
189
|
repairLauncher: 'Check / repair Launcher',
|
|
136
190
|
repairAnalysisSkill: 'Check / repair analysis Skill',
|
|
191
|
+
repairSemanticQuerySkill: 'Check / repair semantic query Skill',
|
|
137
192
|
diagnostics: 'Export diagnostics',
|
|
138
193
|
diagnosticsSaved: 'Diagnostics saved',
|
|
139
194
|
start: 'Start Runtime',
|
|
@@ -145,10 +200,20 @@ window.__ModuleLoader__.load({
|
|
|
145
200
|
progressCli: 'Preparing CLI',
|
|
146
201
|
progressLauncher: 'Preparing Launcher',
|
|
147
202
|
progressAnalysis: 'Preparing analysis Skill',
|
|
203
|
+
progressSemanticQuery: 'Preparing semantic query Skill',
|
|
148
204
|
progressSkills: 'Registering DSH Skills',
|
|
149
205
|
progressState: 'Writing install state',
|
|
150
206
|
progressComplete: 'Initialization complete',
|
|
207
|
+
progressRuntimePreflight: 'Checking Runtime state',
|
|
208
|
+
progressRuntimeLauncher: 'Starting Java Launcher',
|
|
209
|
+
progressRuntimeReadiness: 'Waiting for Runtime readiness',
|
|
210
|
+
progressRuntimeCapabilities: 'Verifying Runtime capabilities',
|
|
211
|
+
progressRuntimeState: 'Saving Runtime state',
|
|
212
|
+
progressRuntimeComplete: 'Runtime ready',
|
|
151
213
|
progressFiles: 'files',
|
|
214
|
+
progressElapsed: 'elapsed',
|
|
215
|
+
progressTimeout: 'timeout',
|
|
216
|
+
progressSeconds: 's',
|
|
152
217
|
managedPrivate: 'Foggy private',
|
|
153
218
|
configuredOverride: 'Configured override',
|
|
154
219
|
accepted: 'The operation started; this page will refresh automatically.',
|
|
@@ -165,6 +230,10 @@ window.__ModuleLoader__.load({
|
|
|
165
230
|
noOnboarding: 'No database onboarding record yet. Invoke the Foggy onboarding Skill in a conversation.',
|
|
166
231
|
projectRoot: 'Workspace',
|
|
167
232
|
resumeInChat: 'Resume incomplete steps from a conversation in the matching workspace.',
|
|
233
|
+
queryWarningTitle: 'Query input was normalized',
|
|
234
|
+
queryWarningSummary: 'In WARN mode, Runtime ignored unknown query properties and continued. Review the warning codes and evidence before deciding whether to change the model or request.',
|
|
235
|
+
queryWarningCodes: 'Warning codes',
|
|
236
|
+
queryWarningEvidence: 'Evidence directory',
|
|
168
237
|
stepPlanned: 'Plan',
|
|
169
238
|
stepDatasourceConfigured: 'Database connection',
|
|
170
239
|
stepDatasourceVerified: 'Connection test',
|
|
@@ -174,6 +243,21 @@ window.__ModuleLoader__.load({
|
|
|
174
243
|
stepSemanticPublished: 'Publish',
|
|
175
244
|
stepSemanticVerified: 'First query',
|
|
176
245
|
runtimeUrl: 'Runtime URL',
|
|
246
|
+
runtimeSettingsTitle: 'Runtime connection settings',
|
|
247
|
+
runtimePort: 'Runtime port',
|
|
248
|
+
runtimePortHint: 'The port is persisted so the CLI and Skills use one stable Runtime URL.',
|
|
249
|
+
configuredRuntimeUrl: 'Next startup URL',
|
|
250
|
+
saveRuntimePort: 'Save port',
|
|
251
|
+
runtimePortSaved: 'Runtime port saved',
|
|
252
|
+
stopToChangePort: 'Stop Runtime before changing its port.',
|
|
253
|
+
errorTitle: 'Foggy operation failed',
|
|
254
|
+
portUnavailableTitle: 'Runtime port is already in use',
|
|
255
|
+
portUnavailableMessage: 'is already used by another application or Windows port proxy, so Runtime cannot start.',
|
|
256
|
+
portUnavailableHelp: 'Save a different port and start again, or release the application or Windows port proxy using this port.',
|
|
257
|
+
portInvalidTitle: 'Invalid port setting',
|
|
258
|
+
portInvalidMessage: 'The current value cannot be saved as the Runtime port.',
|
|
259
|
+
portInvalidHelp: 'Enter an integer port between 1024 and 65535.',
|
|
260
|
+
settingsInvalidTitle: 'Runtime settings file is invalid',
|
|
177
261
|
nextTitle: 'Next configuration',
|
|
178
262
|
nextCopy: 'Skills are provided to every workspace through the native DeepSeek Harness registry, and each session uses its own working directory. After Runtime starts, the database connection and semantic-layer wizard comes next.',
|
|
179
263
|
beta: 'Beta',
|
|
@@ -188,8 +272,10 @@ window.__ModuleLoader__.load({
|
|
|
188
272
|
.foggy-actions{display:flex;gap:8px;flex-wrap:wrap}.foggy-button{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;border-radius:8px;padding:7px 12px;cursor:pointer}.foggy-button:hover{background:var(--dsw-alias-interactive-bg-hover)}.foggy-button:disabled{opacity:.55;cursor:not-allowed}.foggy-button-primary{border-color:var(--dsw-alias-state-business-primary);background:var(--dsw-alias-state-business-primary);color:white}
|
|
189
273
|
.foggy-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}.foggy-card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;padding:13px 14px}.foggy-card-head{display:flex;justify-content:space-between;gap:8px}.foggy-card strong{font-size:13px}.foggy-badge{font-size:11px;color:var(--dsw-alias-label-secondary)}.foggy-card code{display:block;margin-top:8px;color:var(--dsw-alias-label-tertiary);font-size:11px;overflow-wrap:anywhere}
|
|
190
274
|
.foggy-progress{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;padding:13px 14px}.foggy-progress-head{display:flex;align-items:center;justify-content:space-between;gap:12px;font-size:13px}.foggy-progress-head strong{font-weight:600}.foggy-progress-percent{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums}.foggy-progress-track{height:8px;margin-top:10px;border-radius:999px;background:var(--dsw-alias-bg-layer-1);overflow:hidden}.foggy-progress-fill{height:100%;border-radius:inherit;background:var(--dsw-alias-state-business-primary);transition:width .25s ease}.foggy-progress-meta{display:flex;justify-content:space-between;gap:12px;margin-top:8px;color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}.foggy-progress-file{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
191
|
-
.foggy-
|
|
192
|
-
|
|
275
|
+
.foggy-runtime-settings{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;padding:14px}.foggy-runtime-settings h4{margin:0;font-size:13px}.foggy-runtime-settings-row{display:grid;grid-template-columns:minmax(150px,220px) minmax(0,1fr) auto;align-items:end;gap:10px;margin-top:10px}.foggy-field{display:flex;flex-direction:column;gap:6px}.foggy-field label{font-size:12px;font-weight:600}.foggy-input{width:100%;box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;font-variant-numeric:tabular-nums;padding:8px 10px;outline:none}.foggy-input:focus{border-color:var(--dsw-alias-state-business-primary);box-shadow:0 0 0 3px color-mix(in srgb,var(--dsw-alias-state-business-primary) 18%,transparent)}.foggy-input[aria-invalid=true]{border-color:var(--dsw-alias-state-error-primary);box-shadow:0 0 0 3px color-mix(in srgb,var(--dsw-alias-state-error-primary) 14%,transparent)}.foggy-setting-meta{display:flex;flex-direction:column;gap:4px;min-width:0;color:var(--dsw-alias-label-tertiary);font-size:11px}.foggy-setting-meta code{overflow-wrap:anywhere;color:var(--dsw-alias-label-secondary)}
|
|
276
|
+
.foggy-error-panel{border:2px solid var(--dsw-alias-state-error-primary);background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 10%,var(--dsw-alias-bg-layer-3));border-radius:10px;padding:13px 14px;box-shadow:0 6px 18px color-mix(in srgb,var(--dsw-alias-state-error-primary) 13%,transparent)}.foggy-error-panel-head{display:flex;align-items:center;gap:8px;color:var(--dsw-alias-state-error-primary);font-size:14px;font-weight:700}.foggy-error-icon{display:grid;place-items:center;width:22px;height:22px;border-radius:50%;background:var(--dsw-alias-state-error-primary);color:white;font-size:14px}.foggy-error-panel p{margin:7px 0 0;color:var(--dsw-alias-label-primary);font-size:13px;line-height:20px;overflow-wrap:anywhere}.foggy-error-help{color:var(--dsw-alias-state-error-primary)!important;font-weight:600}
|
|
277
|
+
.foggy-message{margin:0;font-size:13px;line-height:20px;color:var(--dsw-alias-label-secondary)}.foggy-message[data-error=true]{color:var(--dsw-alias-state-error-primary)}.foggy-paths,.foggy-next,.foggy-section{border-top:1px solid var(--dsw-alias-border-l2);padding-top:14px}.foggy-paths h4,.foggy-next h4,.foggy-section h4{margin:0 0 8px;font-size:13px}.foggy-paths dl{display:grid;grid-template-columns:100px minmax(0,1fr);gap:6px 10px;margin:0}.foggy-paths dt{color:var(--dsw-alias-label-tertiary);font-size:12px}.foggy-paths dd{margin:0;min-width:0;overflow-wrap:anywhere;font-family:var(--ds-font-family-code);font-size:11px}.foggy-next p,.foggy-section p{margin:0;color:var(--dsw-alias-label-secondary);font-size:13px;line-height:20px}.foggy-section-actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px}.foggy-onboarding-head{display:flex;justify-content:space-between;gap:12px;align-items:start}.foggy-onboarding-head code{font-size:11px;color:var(--dsw-alias-label-tertiary);overflow-wrap:anywhere}.foggy-step-list{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:7px;margin-top:10px}.foggy-step{border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:8px;font-size:11px;color:var(--dsw-alias-label-tertiary)}.foggy-step[data-state=completed]{border-color:color-mix(in srgb,var(--dsw-alias-state-success-primary) 45%,var(--dsw-alias-border-l2));color:var(--dsw-alias-state-success-primary)}.foggy-step[data-state=failed],.foggy-step[data-state=invalid]{border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary)}.foggy-warning{border:1px solid color-mix(in srgb,var(--dsw-alias-state-warning-primary) 55%,var(--dsw-alias-border-l2));background:color-mix(in srgb,var(--dsw-alias-state-warning-primary) 8%,transparent);border-radius:10px;padding:12px}.foggy-query-warning{margin-top:10px}.foggy-query-warning p+p{margin-top:5px}.foggy-query-warning code{overflow-wrap:anywhere}
|
|
278
|
+
@media (width<=680px){.foggy-grid{grid-template-columns:minmax(0,1fr)}.foggy-runtime-settings-row{grid-template-columns:minmax(0,1fr)}.foggy-runtime-settings-row .foggy-button{width:100%}}
|
|
193
279
|
`
|
|
194
280
|
|
|
195
281
|
if (typeof document !== 'undefined' && !document.querySelector('style[data-foggy-settings]')) {
|
|
@@ -244,24 +330,37 @@ window.__ModuleLoader__.load({
|
|
|
244
330
|
cli: 'progressCli',
|
|
245
331
|
launcher: 'progressLauncher',
|
|
246
332
|
'analysis-skill': 'progressAnalysis',
|
|
333
|
+
'semantic-query-skill': 'progressSemanticQuery',
|
|
247
334
|
'workspace-skills': 'progressSkills',
|
|
248
335
|
state: 'progressState',
|
|
249
336
|
complete: 'progressComplete',
|
|
337
|
+
'runtime-preflight': 'progressRuntimePreflight',
|
|
338
|
+
'runtime-launcher': 'progressRuntimeLauncher',
|
|
339
|
+
'runtime-readiness': 'progressRuntimeReadiness',
|
|
340
|
+
'runtime-capabilities': 'progressRuntimeCapabilities',
|
|
341
|
+
'runtime-state': 'progressRuntimeState',
|
|
342
|
+
'runtime-complete': 'progressRuntimeComplete',
|
|
250
343
|
}
|
|
251
344
|
const phase = t(phaseKeys[progress.phase] || 'progressWorking')
|
|
345
|
+
const headline = String(progress.phase || '').startsWith('runtime-') ? phase : progress.message || phase
|
|
252
346
|
const step = progress.step?.index && progress.step?.total
|
|
253
347
|
? `${progress.step.index}/${progress.step.total}`
|
|
254
348
|
: ''
|
|
255
349
|
const files = progress.files?.total
|
|
256
350
|
? `${progress.files.completed || 0}/${progress.files.total} ${t('progressFiles')}`
|
|
257
351
|
: ''
|
|
352
|
+
const elapsed = Number(progress.timing?.elapsedSeconds)
|
|
353
|
+
const timeout = Number(progress.timing?.timeoutSeconds)
|
|
354
|
+
const timing = Number.isFinite(elapsed)
|
|
355
|
+
? `${t('progressElapsed')} ${elapsed}${t('progressSeconds')}${Number.isFinite(timeout) ? ` / ${t('progressTimeout')} ${timeout}${t('progressSeconds')}` : ''}`
|
|
356
|
+
: ''
|
|
258
357
|
return jsxs('div', {
|
|
259
358
|
className: 'foggy-progress',
|
|
260
359
|
role: 'status',
|
|
261
360
|
'aria-live': 'polite',
|
|
262
361
|
children: [
|
|
263
362
|
jsxs('div', { className: 'foggy-progress-head', children: [
|
|
264
|
-
jsx('strong', { children:
|
|
363
|
+
jsx('strong', { children: headline }),
|
|
265
364
|
jsx('span', { className: 'foggy-progress-percent', children: `${percent}%` }),
|
|
266
365
|
] }),
|
|
267
366
|
jsx('div', {
|
|
@@ -275,7 +374,7 @@ window.__ModuleLoader__.load({
|
|
|
275
374
|
}),
|
|
276
375
|
jsxs('div', { className: 'foggy-progress-meta', children: [
|
|
277
376
|
jsx('span', { children: phase }),
|
|
278
|
-
jsx('span', { children: [step, files].filter(Boolean).join(' · ') }),
|
|
377
|
+
jsx('span', { children: [step, files, timing].filter(Boolean).join(' · ') }),
|
|
279
378
|
] }),
|
|
280
379
|
progress.currentFile ? jsx('div', { className: 'foggy-progress-meta foggy-progress-file', title: progress.currentFile, children: progress.currentFile }) : null,
|
|
281
380
|
],
|
|
@@ -313,22 +412,54 @@ window.__ModuleLoader__.load({
|
|
|
313
412
|
title: profile.steps?.[name]?.status || 'pending',
|
|
314
413
|
children: t(label),
|
|
315
414
|
}, name)) }),
|
|
415
|
+
profile.warningCount > 0 ? jsxs('div', { className: 'foggy-warning foggy-query-warning', role: 'status', children: [
|
|
416
|
+
jsx('h4', { children: `${t('queryWarningTitle')} · ${profile.warningCount}` }),
|
|
417
|
+
jsx('p', { children: t('queryWarningSummary') }),
|
|
418
|
+
profile.warningCodes?.length ? jsxs('p', { children: [`${t('queryWarningCodes')}: `, jsx('code', { children: profile.warningCodes.join(', ') })] }) : null,
|
|
419
|
+
profile.warningEvidence ? jsxs('p', { children: [`${t('queryWarningEvidence')}: `, jsx('code', { children: profile.warningEvidence })] }) : null,
|
|
420
|
+
] }) : null,
|
|
316
421
|
profile.next?.status !== 'completed' ? jsx('p', { style: { marginTop: '9px' }, children: t('resumeInChat') }) : null,
|
|
317
422
|
] })
|
|
318
423
|
}
|
|
319
424
|
|
|
425
|
+
function errorPanel(title, message, help) {
|
|
426
|
+
if (!message) return null
|
|
427
|
+
return jsxs('div', {
|
|
428
|
+
className: 'foggy-error-panel',
|
|
429
|
+
role: 'alert',
|
|
430
|
+
'aria-live': 'assertive',
|
|
431
|
+
children: [
|
|
432
|
+
jsxs('div', { className: 'foggy-error-panel-head', children: [
|
|
433
|
+
jsx('span', { className: 'foggy-error-icon', 'aria-hidden': true, children: '!' }),
|
|
434
|
+
jsx('strong', { children: title }),
|
|
435
|
+
] }),
|
|
436
|
+
jsx('p', { children: message }),
|
|
437
|
+
help ? jsx('p', { className: 'foggy-error-help', children: help }) : null,
|
|
438
|
+
],
|
|
439
|
+
})
|
|
440
|
+
}
|
|
441
|
+
|
|
320
442
|
function FoggySettingsTab({ api, t }) {
|
|
321
|
-
const [view, setView] = useState({ phase: 'loading', status: null, message: '', error: false })
|
|
443
|
+
const [view, setView] = useState({ phase: 'loading', status: null, message: '', error: false, errorCode: null })
|
|
444
|
+
const [portDraft, setPortDraft] = useState('')
|
|
445
|
+
const [portDirty, setPortDirty] = useState(false)
|
|
322
446
|
|
|
323
447
|
const refresh = useCallback(async () => {
|
|
324
448
|
setView((current) => ({ ...current, phase: current.status ? 'ready' : 'loading', error: false }))
|
|
325
449
|
try {
|
|
326
450
|
const status = unwrap(await api.status(), 'status')
|
|
327
|
-
setView((current) => ({
|
|
451
|
+
setView((current) => ({
|
|
452
|
+
...current,
|
|
453
|
+
phase: 'ready',
|
|
454
|
+
status,
|
|
455
|
+
error: false,
|
|
456
|
+
message: current.message === t('accepted') && status.operation?.state !== 'running' ? '' : current.message,
|
|
457
|
+
}))
|
|
458
|
+
if (!portDirty) setPortDraft(String(status.runtimeSettings?.port ?? 18166))
|
|
328
459
|
} catch (error) {
|
|
329
460
|
setView((current) => ({ ...current, phase: current.status ? 'ready' : 'error', message: String(error.message || error), error: true }))
|
|
330
461
|
}
|
|
331
|
-
}, [api])
|
|
462
|
+
}, [api, portDirty, t])
|
|
332
463
|
|
|
333
464
|
useEffect(() => { refresh() }, [refresh])
|
|
334
465
|
useEffect(() => {
|
|
@@ -338,18 +469,40 @@ window.__ModuleLoader__.load({
|
|
|
338
469
|
}, [refresh, view.status?.operation?.state])
|
|
339
470
|
|
|
340
471
|
const run = async (method) => {
|
|
341
|
-
setView((current) => ({ ...current, message: t('working'), error: false }))
|
|
472
|
+
setView((current) => ({ ...current, message: t('working'), error: false, errorCode: null }))
|
|
342
473
|
try {
|
|
343
474
|
const result = unwrap(await api[method](), method)
|
|
344
475
|
const message = result.accepted
|
|
345
476
|
? t('accepted')
|
|
346
477
|
: result.path
|
|
347
478
|
? `${t('diagnosticsSaved')}: ${result.path}`
|
|
348
|
-
: ''
|
|
349
|
-
setView((current) => ({ ...current, message, error: result.success === false }))
|
|
479
|
+
: result.error?.message || ''
|
|
480
|
+
setView((current) => ({ ...current, message, error: result.success === false, errorCode: result.error?.code || null }))
|
|
350
481
|
await refresh()
|
|
351
482
|
} catch (error) {
|
|
352
|
-
setView((current) => ({ ...current, message: `${t('actionFailed')}: ${String(error.message || error)}`, error: true }))
|
|
483
|
+
setView((current) => ({ ...current, message: `${t('actionFailed')}: ${String(error.message || error)}`, error: true, errorCode: 'REMOTE_ERROR' }))
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
const saveRuntimePort = async () => {
|
|
488
|
+
const port = Number(portDraft)
|
|
489
|
+
if (!Number.isInteger(port) || port < 1024 || port > 65535) {
|
|
490
|
+
setView((current) => ({ ...current, message: t('portInvalidMessage'), error: true, errorCode: 'RUNTIME_PORT_INVALID' }))
|
|
491
|
+
return
|
|
492
|
+
}
|
|
493
|
+
setView((current) => ({ ...current, message: t('working'), error: false, errorCode: null }))
|
|
494
|
+
try {
|
|
495
|
+
const result = unwrap(await api.saveRuntimeSettings({ port }), 'saveRuntimeSettings')
|
|
496
|
+
if (result.success === false) {
|
|
497
|
+
setView((current) => ({ ...current, message: result.error?.message || t('actionFailed'), error: true, errorCode: result.error?.code || null }))
|
|
498
|
+
return
|
|
499
|
+
}
|
|
500
|
+
setPortDirty(false)
|
|
501
|
+
setPortDraft(String(result.settings.port))
|
|
502
|
+
setView((current) => ({ ...current, message: `${t('runtimePortSaved')}: ${result.settings.runtimeUrl}`, error: false, errorCode: null }))
|
|
503
|
+
await refresh()
|
|
504
|
+
} catch (error) {
|
|
505
|
+
setView((current) => ({ ...current, message: `${t('actionFailed')}: ${String(error.message || error)}`, error: true, errorCode: 'REMOTE_ERROR' }))
|
|
353
506
|
}
|
|
354
507
|
}
|
|
355
508
|
|
|
@@ -361,9 +514,38 @@ window.__ModuleLoader__.load({
|
|
|
361
514
|
|
|
362
515
|
const status = view.status
|
|
363
516
|
const busy = status.operation?.state === 'running'
|
|
517
|
+
const portNumber = Number(portDraft)
|
|
518
|
+
const portValid = Number.isInteger(portNumber) && portNumber >= 1024 && portNumber <= 65535
|
|
364
519
|
const operationError = status.operation?.state === 'failed'
|
|
365
520
|
? status.operation.error || status.operation.result?.error?.message || t('actionFailed')
|
|
366
521
|
: ''
|
|
522
|
+
const operationErrorCode = status.operation?.state === 'failed' ? status.operation.result?.error?.code || null : null
|
|
523
|
+
const settingsError = status.runtimeSettings?.valid === false
|
|
524
|
+
? status.runtimeSettings.error || t('settingsInvalidTitle')
|
|
525
|
+
: ''
|
|
526
|
+
const portConflict = status.runtimeSettings?.conflictApplies ? status.runtimeSettings.lastConflict : null
|
|
527
|
+
const validationError = portDirty && !portValid ? t('portInvalidMessage') : ''
|
|
528
|
+
const rawAlertMessage = validationError || (view.error ? view.message : '') || operationError || settingsError || portConflict?.message || ''
|
|
529
|
+
const alertCode = validationError
|
|
530
|
+
? 'RUNTIME_PORT_INVALID'
|
|
531
|
+
: view.error
|
|
532
|
+
? view.errorCode
|
|
533
|
+
: operationErrorCode || (settingsError ? 'RUNTIME_SETTINGS_INVALID' : portConflict ? 'RUNTIME_PORT_UNAVAILABLE' : null)
|
|
534
|
+
const alertTitle = alertCode === 'RUNTIME_PORT_UNAVAILABLE'
|
|
535
|
+
? t('portUnavailableTitle')
|
|
536
|
+
: alertCode === 'RUNTIME_PORT_INVALID'
|
|
537
|
+
? t('portInvalidTitle')
|
|
538
|
+
: alertCode === 'RUNTIME_SETTINGS_INVALID'
|
|
539
|
+
? t('settingsInvalidTitle')
|
|
540
|
+
: t('errorTitle')
|
|
541
|
+
const alertMessage = alertCode === 'RUNTIME_PORT_UNAVAILABLE'
|
|
542
|
+
? `${t('runtimePort')} ${portConflict?.port ?? status.runtimeSettings?.port ?? portNumber} ${t('portUnavailableMessage')}`
|
|
543
|
+
: rawAlertMessage
|
|
544
|
+
const alertHelp = alertCode === 'RUNTIME_PORT_UNAVAILABLE'
|
|
545
|
+
? t('portUnavailableHelp')
|
|
546
|
+
: alertCode === 'RUNTIME_PORT_INVALID'
|
|
547
|
+
? t('portInvalidHelp')
|
|
548
|
+
: ''
|
|
367
549
|
const progress = busy ? progressPanel(status.operation?.progress, t) : null
|
|
368
550
|
const stateLabels = { 'not-installed': 'notInstalled', degraded: 'degraded', ready: 'ready', running: 'running' }
|
|
369
551
|
return jsxs('section', { className: 'foggy-settings', children: [
|
|
@@ -382,14 +564,53 @@ window.__ModuleLoader__.load({
|
|
|
382
564
|
status.running ? jsx('button', { className: 'foggy-button', type: 'button', disabled: busy, onClick: () => run('runtimeStop'), children: t('stop') }) : null,
|
|
383
565
|
] }),
|
|
384
566
|
] }),
|
|
567
|
+
errorPanel(alertTitle, alertMessage, alertHelp),
|
|
385
568
|
progress,
|
|
386
|
-
busy
|
|
569
|
+
busy ? jsx('p', { className: 'foggy-message', children: t('working') }) : null,
|
|
570
|
+
!busy && view.message && !view.error && !operationError ? jsx('p', { className: 'foggy-message', children: view.message }) : null,
|
|
571
|
+
jsxs('div', { className: 'foggy-runtime-settings', children: [
|
|
572
|
+
jsx('h4', { children: t('runtimeSettingsTitle') }),
|
|
573
|
+
jsxs('div', { className: 'foggy-runtime-settings-row', children: [
|
|
574
|
+
jsxs('div', { className: 'foggy-field', children: [
|
|
575
|
+
jsx('label', { htmlFor: 'foggy-runtime-port', children: t('runtimePort') }),
|
|
576
|
+
jsx('input', {
|
|
577
|
+
id: 'foggy-runtime-port',
|
|
578
|
+
className: 'foggy-input',
|
|
579
|
+
type: 'number',
|
|
580
|
+
min: 1024,
|
|
581
|
+
max: 65535,
|
|
582
|
+
step: 1,
|
|
583
|
+
value: portDraft,
|
|
584
|
+
disabled: busy || status.running,
|
|
585
|
+
'aria-invalid': portDirty && !portValid ? 'true' : undefined,
|
|
586
|
+
onChange: (event) => {
|
|
587
|
+
setPortDraft(event.target.value)
|
|
588
|
+
setPortDirty(true)
|
|
589
|
+
setView((current) => ({ ...current, message: '', error: false, errorCode: null }))
|
|
590
|
+
},
|
|
591
|
+
}),
|
|
592
|
+
] }),
|
|
593
|
+
jsxs('div', { className: 'foggy-setting-meta', children: [
|
|
594
|
+
jsx('span', { children: t('runtimePortHint') }),
|
|
595
|
+
jsx('code', { children: `${t('configuredRuntimeUrl')}: http://127.0.0.1:${portValid ? portNumber : '—'}` }),
|
|
596
|
+
status.running ? jsx('span', { children: t('stopToChangePort') }) : null,
|
|
597
|
+
] }),
|
|
598
|
+
jsx('button', {
|
|
599
|
+
className: 'foggy-button foggy-button-primary',
|
|
600
|
+
type: 'button',
|
|
601
|
+
disabled: busy || status.running || !portDirty || !portValid,
|
|
602
|
+
onClick: saveRuntimePort,
|
|
603
|
+
children: t('saveRuntimePort'),
|
|
604
|
+
}),
|
|
605
|
+
] }),
|
|
606
|
+
] }),
|
|
387
607
|
jsxs('div', { className: 'foggy-grid', children: [
|
|
388
608
|
componentCard(t('python'), status.components.python, 'tool', t),
|
|
389
609
|
componentCard(t('java'), status.components.java, 'tool', t),
|
|
390
610
|
componentCard(t('cli'), status.components.cli, 'runtime', t),
|
|
391
611
|
componentCard(t('launcher'), status.components.launcher, 'runtime', t),
|
|
392
612
|
componentCard(t('analysisSkill'), status.components.analysisSkill, 'runtime', t),
|
|
613
|
+
componentCard(t('semanticQuerySkill'), status.components.semanticQuerySkill, 'runtime', t),
|
|
393
614
|
componentCard(t('onboardingSkill'), status.components.onboardingSkill, 'runtime', t),
|
|
394
615
|
componentCard(t('skillRegistry'), { installed: status.components.onboardingSkill?.provider === 'foggy-managed-skills', version: 'native' }, 'runtime', t),
|
|
395
616
|
] }),
|
|
@@ -400,6 +621,7 @@ window.__ModuleLoader__.load({
|
|
|
400
621
|
jsx('button', { className: 'foggy-button', type: 'button', disabled: busy, onClick: () => run('repairCli'), children: t('repairCli') }),
|
|
401
622
|
jsx('button', { className: 'foggy-button', type: 'button', disabled: busy, onClick: () => run('repairLauncher'), children: t('repairLauncher') }),
|
|
402
623
|
jsx('button', { className: 'foggy-button', type: 'button', disabled: busy, onClick: () => run('repairAnalysisSkill'), children: t('repairAnalysisSkill') }),
|
|
624
|
+
jsx('button', { className: 'foggy-button', type: 'button', disabled: busy, onClick: () => run('repairSemanticQuerySkill'), children: t('repairSemanticQuerySkill') }),
|
|
403
625
|
] }),
|
|
404
626
|
] }),
|
|
405
627
|
status.profileMigration?.pendingCount || status.profileMigration?.conflictCount ? jsxs('div', { className: 'foggy-warning', children: [
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { open } from 'node:fs/promises'
|
|
2
|
+
import { isAbsolute, join, relative, resolve, sep } from 'node:path'
|
|
3
|
+
|
|
4
|
+
const DEFAULT_MAX_BYTES = 32 * 1024
|
|
5
|
+
const DEFAULT_MAX_LINES = 120
|
|
6
|
+
const ANSI_ESCAPE = /\u001b(?:\[[0-?]*[ -/]*[@-~]|\][^\u0007]*(?:\u0007|\u001b\\))/g
|
|
7
|
+
const BEARER_SECRET = /\bBearer\s+[A-Za-z0-9._~+/=-]{8,}/gi
|
|
8
|
+
const API_KEY_SECRET = /\bsk-[A-Za-z0-9._-]{12,}/g
|
|
9
|
+
const JDBC_URL = /\bjdbc:[^\s"'<>]+/gi
|
|
10
|
+
const URL_CREDENTIALS = /(\b[a-z][a-z0-9+.-]*:\/\/)[^\s/@:]+:[^\s/@]+@/gi
|
|
11
|
+
const SENSITIVE_QUERY_VALUE = /([?&](?:password|passwd|pwd|secret|token|api[_-]?key|auth[_-]?code)=)[^&#\s]*/gi
|
|
12
|
+
const SENSITIVE_ASSIGNMENT = /\b([A-Za-z0-9_.-]*(?:password|passwd|pwd|secret|token|api[_-]?key|authorization|auth[_-]?code)[A-Za-z0-9_.-]*)(\s*[:=]\s*)(?:"[^"]*"|'[^']*'|[^\s,;]+)/gi
|
|
13
|
+
|
|
14
|
+
export function sanitizeDiagnosticText(value) {
|
|
15
|
+
return String(value ?? '')
|
|
16
|
+
.replace(ANSI_ESCAPE, '')
|
|
17
|
+
.replace(BEARER_SECRET, 'Bearer [REDACTED]')
|
|
18
|
+
.replace(API_KEY_SECRET, '[REDACTED_API_KEY]')
|
|
19
|
+
.replace(JDBC_URL, 'jdbc:[REDACTED_CONNECTION_URL]')
|
|
20
|
+
.replace(URL_CREDENTIALS, '$1[REDACTED]@')
|
|
21
|
+
.replace(SENSITIVE_QUERY_VALUE, '$1[REDACTED]')
|
|
22
|
+
.replace(SENSITIVE_ASSIGNMENT, '$1$2[REDACTED]')
|
|
23
|
+
.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, '')
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function isInside(root, candidate) {
|
|
27
|
+
const value = relative(resolve(root), resolve(candidate))
|
|
28
|
+
return value !== '' && value !== '..' && !value.startsWith(`..${sep}`) && !isAbsolute(value)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function readDiagnosticLogTail(path, {
|
|
32
|
+
allowedRoot,
|
|
33
|
+
maxBytes = DEFAULT_MAX_BYTES,
|
|
34
|
+
maxLines = DEFAULT_MAX_LINES,
|
|
35
|
+
} = {}) {
|
|
36
|
+
if (!path || !allowedRoot || !isInside(allowedRoot, path)) {
|
|
37
|
+
return { sanitizedTail: '', tailTruncated: false, tailError: 'LOG_PATH_OUTSIDE_RUNTIME_ROOT' }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let handle
|
|
41
|
+
try {
|
|
42
|
+
handle = await open(path, 'r')
|
|
43
|
+
const details = await handle.stat()
|
|
44
|
+
const bytesToRead = Math.min(details.size, maxBytes)
|
|
45
|
+
const buffer = Buffer.alloc(bytesToRead)
|
|
46
|
+
if (bytesToRead > 0) await handle.read(buffer, 0, bytesToRead, details.size - bytesToRead)
|
|
47
|
+
let text = buffer.toString('utf8')
|
|
48
|
+
let truncated = details.size > bytesToRead
|
|
49
|
+
if (truncated) {
|
|
50
|
+
const firstLineEnd = text.indexOf('\n')
|
|
51
|
+
if (firstLineEnd >= 0) text = text.slice(firstLineEnd + 1)
|
|
52
|
+
}
|
|
53
|
+
const lines = text.split(/\r?\n/)
|
|
54
|
+
if (lines.length > maxLines) {
|
|
55
|
+
text = lines.slice(-maxLines).join('\n')
|
|
56
|
+
truncated = true
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
sanitizedTail: sanitizeDiagnosticText(text).trim(),
|
|
60
|
+
tailTruncated: truncated,
|
|
61
|
+
tailBytesRead: bytesToRead,
|
|
62
|
+
}
|
|
63
|
+
} catch (error) {
|
|
64
|
+
return {
|
|
65
|
+
sanitizedTail: '',
|
|
66
|
+
tailTruncated: false,
|
|
67
|
+
tailError: error?.code || 'LOG_READ_FAILED',
|
|
68
|
+
}
|
|
69
|
+
} finally {
|
|
70
|
+
await handle?.close()
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export async function enrichRuntimeStartFailure(failure, dataRoot) {
|
|
75
|
+
if (!failure || typeof failure !== 'object') return failure
|
|
76
|
+
const runtimeRoot = join(dataRoot, 'runtime')
|
|
77
|
+
const logs = Array.isArray(failure.logs)
|
|
78
|
+
? await Promise.all(failure.logs.map(async (entry) => ({
|
|
79
|
+
...entry,
|
|
80
|
+
...(entry?.exists && entry?.path
|
|
81
|
+
? await readDiagnosticLogTail(entry.path, { allowedRoot: runtimeRoot })
|
|
82
|
+
: {}),
|
|
83
|
+
})))
|
|
84
|
+
: []
|
|
85
|
+
return { ...failure, logs }
|
|
86
|
+
}
|