@foggy-projects/deepseek-harness-plugin 0.4.0-beta.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.
Files changed (42) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +81 -0
  3. package/THIRD-PARTY-RUNTIME-NOTICES.md +16 -0
  4. package/cordis.patch.yml +6 -0
  5. package/docs/PUBLIC-BETA-READINESS.md +57 -0
  6. package/docs/WINDOWS-BETA-ACCEPTANCE.md +75 -0
  7. package/experience/linux/README.md +53 -0
  8. package/experience/linux/prepare.sh +188 -0
  9. package/lib/atomic-json.js +41 -0
  10. package/lib/client.js +453 -0
  11. package/lib/index.js +467 -0
  12. package/lib/python-runtime.js +339 -0
  13. package/lib/remote-descriptor.js +30 -0
  14. package/lib/remote.js +8 -0
  15. package/lib/skill-provider.js +111 -0
  16. package/lib/typert.js +9 -0
  17. package/lib/version.js +20 -0
  18. package/package.json +63 -0
  19. package/skills/foggy-deepseek-onboarding/SKILL.md +137 -0
  20. package/skills/foggy-deepseek-onboarding/assets/connection.schema.json +50 -0
  21. package/skills/foggy-deepseek-onboarding/assets/datasource.example.json +13 -0
  22. package/skills/foggy-deepseek-onboarding/assets/env.example +14 -0
  23. package/skills/foggy-deepseek-onboarding/assets/onboarding-state.schema.json +42 -0
  24. package/skills/foggy-deepseek-onboarding/assets/semantic-plan.example.json +8 -0
  25. package/skills/foggy-deepseek-onboarding/assets/semantic-plan.schema.json +21 -0
  26. package/skills/foggy-deepseek-onboarding/assets/versions.json +137 -0
  27. package/skills/foggy-deepseek-onboarding/references/onboarding-workflow.md +156 -0
  28. package/skills/foggy-deepseek-onboarding/scripts/doctor.ps1 +4 -0
  29. package/skills/foggy-deepseek-onboarding/scripts/doctor.sh +4 -0
  30. package/skills/foggy-deepseek-onboarding/scripts/install.ps1 +4 -0
  31. package/skills/foggy-deepseek-onboarding/scripts/install.sh +4 -0
  32. package/skills/foggy-deepseek-onboarding/scripts/invoke-onboarding.ps1 +30 -0
  33. package/skills/foggy-deepseek-onboarding/scripts/invoke-onboarding.sh +24 -0
  34. package/skills/foggy-deepseek-onboarding/scripts/onboard.ps1 +4 -0
  35. package/skills/foggy-deepseek-onboarding/scripts/onboard.sh +4 -0
  36. package/skills/foggy-deepseek-onboarding/scripts/onboarding.py +3027 -0
  37. package/skills/foggy-deepseek-onboarding/scripts/runtime-start.ps1 +4 -0
  38. package/skills/foggy-deepseek-onboarding/scripts/runtime-start.sh +4 -0
  39. package/skills/foggy-deepseek-onboarding/scripts/runtime-stop.ps1 +4 -0
  40. package/skills/foggy-deepseek-onboarding/scripts/runtime-stop.sh +4 -0
  41. package/skills/foggy-deepseek-onboarding/scripts/uninstall.ps1 +4 -0
  42. package/skills/foggy-deepseek-onboarding/scripts/uninstall.sh +4 -0
package/lib/client.js ADDED
@@ -0,0 +1,453 @@
1
+ window.__ModuleLoader__.load({
2
+ id: '@foggy-projects/deepseek-harness-plugin',
3
+ factory: (require) => {
4
+ const module = { exports: {} }
5
+ const exports = module.exports
6
+ const { jsx, jsxs } = require('react/jsx-runtime')
7
+ const { useCallback, useEffect, useState } = require('react')
8
+
9
+ const NS = 'settings.foggy'
10
+ const passthroughSchema = { parse: (value) => value }
11
+ const TYPERT_REMOTE = {
12
+ package: '@foggy-projects/deepseek-harness-plugin',
13
+ descriptors: [
14
+ 'status', 'plan', 'initialize', 'repair', 'repairPython', 'repairCli', 'repairLauncher', 'repairAnalysisSkill',
15
+ 'migrateProfiles', 'diagnostics', 'runtimeStart', 'runtimeStop',
16
+ ].map((method) => ({
17
+ id: `@foggy-projects/deepseek-harness-plugin#foggyIntegration/${method}`,
18
+ service: 'foggyIntegration',
19
+ namespace: 'foggyIntegration',
20
+ method,
21
+ invocation: { kind: 'direct' },
22
+ parameters: [],
23
+ result: {
24
+ mode: 'strict',
25
+ typeSymbol: '@foggy-projects/deepseek-harness-plugin#FoggyIntegrationResult',
26
+ schema: passthroughSchema,
27
+ },
28
+ sourceLocation: { file: 'lib/index.js', line: 1, column: 1 },
29
+ })),
30
+ }
31
+
32
+ const zh = {
33
+ tab: 'Foggy 数据分析',
34
+ title: 'Foggy Java 数据分析引擎',
35
+ intro: '在 DeepSeek Harness 内管理 Foggy 私有 Python、CLI、Launcher、Skills 和本地 Runtime。大型组件在首次初始化时按需下载。',
36
+ refresh: '刷新状态',
37
+ loading: '正在读取 Foggy 状态…',
38
+ failed: '暂时无法读取 Foggy 状态。',
39
+ retry: '重试',
40
+ notInstalled: '尚未初始化',
41
+ degraded: '需要修复',
42
+ ready: '已就绪',
43
+ running: 'Runtime 运行中',
44
+ cli: 'CLI',
45
+ launcher: 'Launcher',
46
+ analysisSkill: '分析 Skill',
47
+ onboardingSkill: '引导 Skill',
48
+ skillRegistry: 'DSH Skill 注册',
49
+ python: 'Python',
50
+ java: 'Java',
51
+ installed: '已安装',
52
+ missing: '未安装',
53
+ available: '可用',
54
+ unavailable: '不可用',
55
+ initialize: '初始化 Foggy',
56
+ repair: '重新下载 / 修复',
57
+ repairTitle: '分组件检查与修复',
58
+ repairPython: '检查 / 修复私有 Python',
59
+ repairCli: '检查 / 修复 CLI',
60
+ repairLauncher: '检查 / 修复 Launcher',
61
+ repairAnalysisSkill: '检查 / 修复分析 Skill',
62
+ diagnostics: '导出诊断报告',
63
+ diagnosticsSaved: '诊断报告已保存',
64
+ start: '启动 Runtime',
65
+ stop: '停止 Runtime',
66
+ working: '操作进行中…',
67
+ progressWorking: '正在初始化 Foggy…',
68
+ progressPreflight: '检查运行环境',
69
+ progressPython: '准备私有 Python',
70
+ progressCli: '准备 CLI',
71
+ progressLauncher: '准备 Launcher',
72
+ progressAnalysis: '准备分析 Skill',
73
+ progressSkills: '注册 DSH Skills',
74
+ progressState: '写入安装状态',
75
+ progressComplete: '初始化完成',
76
+ progressFiles: '文件',
77
+ managedPrivate: 'Foggy 私有',
78
+ configuredOverride: '外部指定',
79
+ accepted: '操作已开始;页面会自动刷新状态。',
80
+ actionFailed: '操作失败',
81
+ roots: '本地目录',
82
+ installRoot: '组件目录',
83
+ dataRoot: '数据目录',
84
+ profileStore: 'CLI Profile',
85
+ profileMigrationTitle: '旧 Profile 迁移',
86
+ profileMigrationPending: '检测到旧版临时 Profile。迁移只转移连接元数据和密码环境变量引用,不复制密码值。',
87
+ profileMigrationConflict: '旧 Profile 存在冲突或格式问题,请先导出诊断报告。',
88
+ migrateProfiles: '迁移到持久目录',
89
+ onboardingProgress: '数据库与语义层进度',
90
+ noOnboarding: '尚无数据库引导记录;请在对话中调用 Foggy onboarding Skill。',
91
+ projectRoot: '工作区',
92
+ resumeInChat: '未完成步骤应回到对应工作区,在对话中继续。',
93
+ stepPlanned: '规划',
94
+ stepDatasourceConfigured: '数据库连接',
95
+ stepDatasourceVerified: '连接验证',
96
+ stepSchemaDiscovered: 'Schema 发现',
97
+ stepSemanticDrafted: '语义层草拟',
98
+ stepSemanticValidated: '模型校验',
99
+ stepSemanticPublished: '发布',
100
+ stepSemanticVerified: '首次查询',
101
+ runtimeUrl: 'Runtime 地址',
102
+ nextTitle: '后续配置',
103
+ nextCopy: 'Skills 已通过 DeepSeek Harness 原生注册表提供给所有工作区;每个会话直接使用自己的工作目录。Runtime 启动成功后,将继续进入数据库连接与语义层向导。',
104
+ beta: 'Beta',
105
+ }
106
+
107
+ const en = {
108
+ tab: 'Foggy Data Analysis',
109
+ title: 'Foggy Java Data Analysis Engine',
110
+ intro: 'Manage Foggy private Python, CLI, Launcher, Skills, and the local Runtime inside DeepSeek Harness. Large components are downloaded on first initialization.',
111
+ refresh: 'Refresh status',
112
+ loading: 'Reading Foggy status…',
113
+ failed: 'Foggy status is temporarily unavailable.',
114
+ retry: 'Retry',
115
+ notInstalled: 'Not initialized',
116
+ degraded: 'Repair required',
117
+ ready: 'Ready',
118
+ running: 'Runtime running',
119
+ cli: 'CLI',
120
+ launcher: 'Launcher',
121
+ analysisSkill: 'Analysis Skill',
122
+ onboardingSkill: 'Onboarding Skill',
123
+ skillRegistry: 'DSH Skill registry',
124
+ python: 'Python',
125
+ java: 'Java',
126
+ installed: 'Installed',
127
+ missing: 'Missing',
128
+ available: 'Available',
129
+ unavailable: 'Unavailable',
130
+ initialize: 'Initialize Foggy',
131
+ repair: 'Re-download / Repair',
132
+ repairTitle: 'Component checks and repair',
133
+ repairPython: 'Check / repair private Python',
134
+ repairCli: 'Check / repair CLI',
135
+ repairLauncher: 'Check / repair Launcher',
136
+ repairAnalysisSkill: 'Check / repair analysis Skill',
137
+ diagnostics: 'Export diagnostics',
138
+ diagnosticsSaved: 'Diagnostics saved',
139
+ start: 'Start Runtime',
140
+ stop: 'Stop Runtime',
141
+ working: 'Operation in progress…',
142
+ progressWorking: 'Initializing Foggy…',
143
+ progressPreflight: 'Checking prerequisites',
144
+ progressPython: 'Preparing private Python',
145
+ progressCli: 'Preparing CLI',
146
+ progressLauncher: 'Preparing Launcher',
147
+ progressAnalysis: 'Preparing analysis Skill',
148
+ progressSkills: 'Registering DSH Skills',
149
+ progressState: 'Writing install state',
150
+ progressComplete: 'Initialization complete',
151
+ progressFiles: 'files',
152
+ managedPrivate: 'Foggy private',
153
+ configuredOverride: 'Configured override',
154
+ accepted: 'The operation started; this page will refresh automatically.',
155
+ actionFailed: 'Operation failed',
156
+ roots: 'Local directories',
157
+ installRoot: 'Components',
158
+ dataRoot: 'Data',
159
+ profileStore: 'CLI profiles',
160
+ profileMigrationTitle: 'Legacy profile migration',
161
+ profileMigrationPending: 'Legacy temporary profiles were found. Migration transfers connection metadata and password environment-variable references, never password values.',
162
+ profileMigrationConflict: 'A legacy profile has a conflict or invalid format. Export diagnostics before continuing.',
163
+ migrateProfiles: 'Move to persistent store',
164
+ onboardingProgress: 'Database and semantic-layer progress',
165
+ noOnboarding: 'No database onboarding record yet. Invoke the Foggy onboarding Skill in a conversation.',
166
+ projectRoot: 'Workspace',
167
+ resumeInChat: 'Resume incomplete steps from a conversation in the matching workspace.',
168
+ stepPlanned: 'Plan',
169
+ stepDatasourceConfigured: 'Database connection',
170
+ stepDatasourceVerified: 'Connection test',
171
+ stepSchemaDiscovered: 'Schema discovery',
172
+ stepSemanticDrafted: 'Semantic draft',
173
+ stepSemanticValidated: 'Model validation',
174
+ stepSemanticPublished: 'Publish',
175
+ stepSemanticVerified: 'First query',
176
+ runtimeUrl: 'Runtime URL',
177
+ nextTitle: 'Next configuration',
178
+ 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
+ beta: 'Beta',
180
+ }
181
+
182
+ const css = `
183
+ .foggy-settings{width:100%;max-width:820px;color:var(--dsw-alias-label-primary);display:flex;flex-direction:column;gap:16px}
184
+ .foggy-hero{border:1px solid var(--dsw-alias-border-l2);background:linear-gradient(135deg,color-mix(in srgb,var(--dsw-alias-state-business-primary) 10%,var(--dsw-alias-bg-layer-3)),var(--dsw-alias-bg-layer-3));border-radius:12px;padding:18px}
185
+ .foggy-title-row{display:flex;align-items:center;gap:10px}.foggy-title-row h3{margin:0;font-size:18px;line-height:26px}.foggy-beta{font-size:11px;padding:2px 7px;border-radius:999px;background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 14%,transparent);color:var(--dsw-alias-state-business-primary)}
186
+ .foggy-intro{margin:8px 0 0;color:var(--dsw-alias-label-secondary);font-size:13px;line-height:20px}.foggy-toolbar{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}
187
+ .foggy-state{display:flex;align-items:center;gap:8px;font-size:14px;font-weight:600}.foggy-dot{width:9px;height:9px;border-radius:50%;background:var(--dsw-alias-label-tertiary)}.foggy-dot[data-state=ready],.foggy-dot[data-state=running]{background:var(--dsw-alias-state-success-primary)}.foggy-dot[data-state=degraded]{background:var(--dsw-alias-state-error-primary)}
188
+ .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
+ .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
+ .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-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}
192
+ @media (width<=680px){.foggy-grid{grid-template-columns:minmax(0,1fr)}}
193
+ `
194
+
195
+ if (typeof document !== 'undefined' && !document.querySelector('style[data-foggy-settings]')) {
196
+ const tag = document.createElement('style')
197
+ tag.dataset.foggySettings = 'true'
198
+ tag.textContent = css
199
+ document.head.appendChild(tag)
200
+ }
201
+
202
+ function unwrap(response, method) {
203
+ if (!response?.ok) {
204
+ const error = response?.error
205
+ throw new Error(error ? `${error.code}: ${error.message}` : `${method} failed`)
206
+ }
207
+ return response.value
208
+ }
209
+
210
+ function componentCard(label, component, kind, t) {
211
+ const available = kind === 'runtime' ? component?.installed : component?.available
212
+ const stateText = kind === 'runtime'
213
+ ? t(available ? 'installed' : 'missing')
214
+ : t(available ? 'available' : 'unavailable')
215
+ const source = component?.source === 'managed'
216
+ ? t('managedPrivate')
217
+ : component?.source === 'override'
218
+ ? t('configuredOverride')
219
+ : null
220
+ const detail = [
221
+ component?.version || component?.output || '—',
222
+ component?.minimum ? `≥${component.minimum}` : null,
223
+ source,
224
+ ].filter(Boolean).join(' · ')
225
+ return jsxs('div', {
226
+ className: 'foggy-card',
227
+ children: [
228
+ jsxs('div', { className: 'foggy-card-head', children: [
229
+ jsx('strong', { children: label }),
230
+ jsx('span', { className: 'foggy-badge', children: stateText }),
231
+ ] }),
232
+ jsx('code', { children: detail }),
233
+ component?.path ? jsx('code', { title: component.path, children: component.path }) : null,
234
+ ],
235
+ })
236
+ }
237
+
238
+ function progressPanel(progress, t) {
239
+ if (!progress) return null
240
+ const percent = Math.max(0, Math.min(100, Number(progress.percent) || 0))
241
+ const phaseKeys = {
242
+ preflight: 'progressPreflight',
243
+ python: 'progressPython',
244
+ cli: 'progressCli',
245
+ launcher: 'progressLauncher',
246
+ 'analysis-skill': 'progressAnalysis',
247
+ 'workspace-skills': 'progressSkills',
248
+ state: 'progressState',
249
+ complete: 'progressComplete',
250
+ }
251
+ const phase = t(phaseKeys[progress.phase] || 'progressWorking')
252
+ const step = progress.step?.index && progress.step?.total
253
+ ? `${progress.step.index}/${progress.step.total}`
254
+ : ''
255
+ const files = progress.files?.total
256
+ ? `${progress.files.completed || 0}/${progress.files.total} ${t('progressFiles')}`
257
+ : ''
258
+ return jsxs('div', {
259
+ className: 'foggy-progress',
260
+ role: 'status',
261
+ 'aria-live': 'polite',
262
+ children: [
263
+ jsxs('div', { className: 'foggy-progress-head', children: [
264
+ jsx('strong', { children: progress.message || phase }),
265
+ jsx('span', { className: 'foggy-progress-percent', children: `${percent}%` }),
266
+ ] }),
267
+ jsx('div', {
268
+ className: 'foggy-progress-track',
269
+ role: 'progressbar',
270
+ 'aria-valuemin': 0,
271
+ 'aria-valuemax': 100,
272
+ 'aria-valuenow': percent,
273
+ 'aria-label': phase,
274
+ children: jsx('div', { className: 'foggy-progress-fill', style: { width: `${percent}%` } }),
275
+ }),
276
+ jsxs('div', { className: 'foggy-progress-meta', children: [
277
+ jsx('span', { children: phase }),
278
+ jsx('span', { children: [step, files].filter(Boolean).join(' · ') }),
279
+ ] }),
280
+ progress.currentFile ? jsx('div', { className: 'foggy-progress-meta foggy-progress-file', title: progress.currentFile, children: progress.currentFile }) : null,
281
+ ],
282
+ })
283
+ }
284
+
285
+ function onboardingPanel(onboarding, t) {
286
+ const profile = onboarding?.profiles?.[0]
287
+ if (!profile) return jsxs('div', { className: 'foggy-section', children: [
288
+ jsx('h4', { children: t('onboardingProgress') }),
289
+ jsx('p', { children: t('noOnboarding') }),
290
+ ] })
291
+ const steps = [
292
+ ['planned', 'stepPlanned'],
293
+ ['datasourceConfigured', 'stepDatasourceConfigured'],
294
+ ['datasourceVerified', 'stepDatasourceVerified'],
295
+ ['schemaDiscovered', 'stepSchemaDiscovered'],
296
+ ['semanticDrafted', 'stepSemanticDrafted'],
297
+ ['semanticValidated', 'stepSemanticValidated'],
298
+ ['semanticPublished', 'stepSemanticPublished'],
299
+ ['semanticVerified', 'stepSemanticVerified'],
300
+ ]
301
+ const percent = Math.round(((profile.completedSteps || 0) / Math.max(profile.totalSteps || 8, 1)) * 100)
302
+ return jsxs('div', { className: 'foggy-section', children: [
303
+ jsxs('div', { className: 'foggy-onboarding-head', children: [
304
+ jsxs('div', { children: [
305
+ jsx('h4', { children: `${t('onboardingProgress')} · ${profile.profile}` }),
306
+ jsx('code', { children: `${t('projectRoot')}: ${profile.projectRoot || '—'}` }),
307
+ ] }),
308
+ jsx('span', { className: 'foggy-progress-percent', children: `${percent}%` }),
309
+ ] }),
310
+ jsx('div', { className: 'foggy-step-list', children: steps.map(([name, label]) => jsx('div', {
311
+ className: 'foggy-step',
312
+ 'data-state': profile.steps?.[name]?.status || 'pending',
313
+ title: profile.steps?.[name]?.status || 'pending',
314
+ children: t(label),
315
+ }, name)) }),
316
+ profile.next?.status !== 'completed' ? jsx('p', { style: { marginTop: '9px' }, children: t('resumeInChat') }) : null,
317
+ ] })
318
+ }
319
+
320
+ function FoggySettingsTab({ api, t }) {
321
+ const [view, setView] = useState({ phase: 'loading', status: null, message: '', error: false })
322
+
323
+ const refresh = useCallback(async () => {
324
+ setView((current) => ({ ...current, phase: current.status ? 'ready' : 'loading', error: false }))
325
+ try {
326
+ const status = unwrap(await api.status(), 'status')
327
+ setView((current) => ({ ...current, phase: 'ready', status, error: false }))
328
+ } catch (error) {
329
+ setView((current) => ({ ...current, phase: current.status ? 'ready' : 'error', message: String(error.message || error), error: true }))
330
+ }
331
+ }, [api])
332
+
333
+ useEffect(() => { refresh() }, [refresh])
334
+ useEffect(() => {
335
+ if (view.status?.operation?.state !== 'running') return undefined
336
+ const timer = setInterval(refresh, 2000)
337
+ return () => clearInterval(timer)
338
+ }, [refresh, view.status?.operation?.state])
339
+
340
+ const run = async (method) => {
341
+ setView((current) => ({ ...current, message: t('working'), error: false }))
342
+ try {
343
+ const result = unwrap(await api[method](), method)
344
+ const message = result.accepted
345
+ ? t('accepted')
346
+ : result.path
347
+ ? `${t('diagnosticsSaved')}: ${result.path}`
348
+ : ''
349
+ setView((current) => ({ ...current, message, error: result.success === false }))
350
+ await refresh()
351
+ } catch (error) {
352
+ setView((current) => ({ ...current, message: `${t('actionFailed')}: ${String(error.message || error)}`, error: true }))
353
+ }
354
+ }
355
+
356
+ if (view.phase === 'loading') return jsx('p', { className: 'foggy-message', children: t('loading') })
357
+ if (view.phase === 'error') return jsxs('div', { className: 'foggy-settings', children: [
358
+ jsx('p', { className: 'foggy-message', 'data-error': true, children: `${t('failed')} ${view.message}` }),
359
+ jsx('button', { className: 'foggy-button', type: 'button', onClick: refresh, children: t('retry') }),
360
+ ] })
361
+
362
+ const status = view.status
363
+ const busy = status.operation?.state === 'running'
364
+ const operationError = status.operation?.state === 'failed'
365
+ ? status.operation.error || status.operation.result?.error?.message || t('actionFailed')
366
+ : ''
367
+ const progress = busy ? progressPanel(status.operation?.progress, t) : null
368
+ const stateLabels = { 'not-installed': 'notInstalled', degraded: 'degraded', ready: 'ready', running: 'running' }
369
+ return jsxs('section', { className: 'foggy-settings', children: [
370
+ jsxs('div', { className: 'foggy-hero', children: [
371
+ jsxs('div', { className: 'foggy-title-row', children: [jsx('h3', { children: t('title') }), jsx('span', { className: 'foggy-beta', children: t('beta') })] }),
372
+ jsx('p', { className: 'foggy-intro', children: t('intro') }),
373
+ ] }),
374
+ jsxs('div', { className: 'foggy-toolbar', children: [
375
+ jsxs('div', { className: 'foggy-state', children: [jsx('span', { className: 'foggy-dot', 'data-state': status.state }), jsx('span', { children: t(stateLabels[status.state] || 'notInstalled') })] }),
376
+ jsxs('div', { className: 'foggy-actions', children: [
377
+ jsx('button', { className: 'foggy-button', type: 'button', disabled: busy, onClick: refresh, children: t('refresh') }),
378
+ jsx('button', { className: 'foggy-button', type: 'button', disabled: busy, onClick: () => run('diagnostics'), children: t('diagnostics') }),
379
+ status.state === 'not-installed' ? jsx('button', { className: 'foggy-button foggy-button-primary', type: 'button', disabled: busy, onClick: () => run('initialize'), children: t('initialize') }) : null,
380
+ status.state !== 'not-installed' ? jsx('button', { className: 'foggy-button', type: 'button', disabled: busy, onClick: () => run('repair'), children: t('repair') }) : null,
381
+ status.installed && !status.running && status.components.java.available ? jsx('button', { className: 'foggy-button foggy-button-primary', type: 'button', disabled: busy, onClick: () => run('runtimeStart'), children: t('start') }) : null,
382
+ status.running ? jsx('button', { className: 'foggy-button', type: 'button', disabled: busy, onClick: () => run('runtimeStop'), children: t('stop') }) : null,
383
+ ] }),
384
+ ] }),
385
+ progress,
386
+ busy || operationError || view.message ? jsx('p', { className: 'foggy-message', 'data-error': operationError || view.error ? 'true' : undefined, children: busy ? t('working') : operationError || view.message }) : null,
387
+ jsxs('div', { className: 'foggy-grid', children: [
388
+ componentCard(t('python'), status.components.python, 'tool', t),
389
+ componentCard(t('java'), status.components.java, 'tool', t),
390
+ componentCard(t('cli'), status.components.cli, 'runtime', t),
391
+ componentCard(t('launcher'), status.components.launcher, 'runtime', t),
392
+ componentCard(t('analysisSkill'), status.components.analysisSkill, 'runtime', t),
393
+ componentCard(t('onboardingSkill'), status.components.onboardingSkill, 'runtime', t),
394
+ componentCard(t('skillRegistry'), { installed: status.components.onboardingSkill?.provider === 'foggy-managed-skills', version: 'native' }, 'runtime', t),
395
+ ] }),
396
+ jsxs('div', { className: 'foggy-section', children: [
397
+ jsx('h4', { children: t('repairTitle') }),
398
+ jsx('div', { className: 'foggy-section-actions', children: [
399
+ jsx('button', { className: 'foggy-button', type: 'button', disabled: busy, onClick: () => run('repairPython'), children: t('repairPython') }),
400
+ jsx('button', { className: 'foggy-button', type: 'button', disabled: busy, onClick: () => run('repairCli'), children: t('repairCli') }),
401
+ jsx('button', { className: 'foggy-button', type: 'button', disabled: busy, onClick: () => run('repairLauncher'), children: t('repairLauncher') }),
402
+ jsx('button', { className: 'foggy-button', type: 'button', disabled: busy, onClick: () => run('repairAnalysisSkill'), children: t('repairAnalysisSkill') }),
403
+ ] }),
404
+ ] }),
405
+ status.profileMigration?.pendingCount || status.profileMigration?.conflictCount ? jsxs('div', { className: 'foggy-warning', children: [
406
+ jsx('h4', { children: t('profileMigrationTitle') }),
407
+ jsx('p', { children: status.profileMigration.conflictCount ? t('profileMigrationConflict') : t('profileMigrationPending') }),
408
+ !status.profileMigration.conflictCount ? jsx('div', { className: 'foggy-section-actions', children: jsx('button', {
409
+ className: 'foggy-button foggy-button-primary', type: 'button', disabled: busy,
410
+ onClick: () => run('migrateProfiles'), children: t('migrateProfiles'),
411
+ }) }) : null,
412
+ ] }) : null,
413
+ onboardingPanel(status.onboarding, t),
414
+ jsxs('div', { className: 'foggy-paths', children: [
415
+ jsx('h4', { children: t('roots') }),
416
+ jsxs('dl', { children: [
417
+ jsx('dt', { children: t('installRoot') }), jsx('dd', { children: status.roots.installRoot }),
418
+ jsx('dt', { children: t('dataRoot') }), jsx('dd', { children: status.roots.dataRoot }),
419
+ jsx('dt', { children: t('profileStore') }), jsx('dd', { children: status.roots.profileStore }),
420
+ status.runtimeUrl ? jsx('dt', { children: t('runtimeUrl') }) : null,
421
+ status.runtimeUrl ? jsx('dd', { children: status.runtimeUrl }) : null,
422
+ ] }),
423
+ ] }),
424
+ jsxs('div', { className: 'foggy-next', children: [jsx('h4', { children: t('nextTitle') }), jsx('p', { children: t('nextCopy') })] }),
425
+ ] })
426
+ }
427
+
428
+ const inject = ['slots', 'locale', 'remote']
429
+
430
+ async function apply(ctx) {
431
+ const disposeRemote = await ctx.remote.$mount(TYPERT_REMOTE)
432
+ ctx.effect(() => disposeRemote, 'foggy: remote contribution')
433
+ ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'foggy: settings dictionaries')
434
+ const t = ctx.locale.bind(NS)
435
+ ctx.inject(['remote.foggyIntegration'], (remoteCtx) => {
436
+ const api = remoteCtx.remote.foggyIntegration
437
+ remoteCtx.slots.inject('settings.plugins.tab', () => remoteCtx.slots.register({
438
+ name: 'settings.plugins.tab',
439
+ id: 'foggy',
440
+ order: 5,
441
+ label: () => t('tab'),
442
+ locale: NS,
443
+ inject: () => ({ api, t }),
444
+ }, FoggySettingsTab))
445
+ })
446
+ }
447
+
448
+ exports.NS = NS
449
+ exports.inject = inject
450
+ exports.apply = apply
451
+ return module.exports
452
+ },
453
+ })