@foggy-projects/deepseek-harness-plugin 0.4.0-beta.13 → 0.4.0-beta.15
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 +24 -14
- package/docs/PUBLIC-BETA-READINESS.md +32 -6
- package/docs/WINDOWS-BETA-ACCEPTANCE.md +28 -14
- package/experience/linux/prepare.sh +1 -1
- package/lib/client.js +191 -24
- package/lib/index.js +66 -0
- package/lib/remote-descriptor.js +15 -2
- package/lib/runtime-settings.js +89 -0
- package/package.json +2 -2
- package/skills/foggy-deepseek-onboarding/SKILL.md +106 -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 +23 -1
- package/skills/foggy-deepseek-onboarding/assets/versions.json +2 -2
- package/skills/foggy-deepseek-onboarding/references/onboarding-workflow.md +113 -123
- package/skills/foggy-deepseek-onboarding/scripts/onboarding.py +200 -27
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ modify `PATH`, or register Python globally. Advanced users may explicitly set
|
|
|
14
14
|
## Local beta installation
|
|
15
15
|
|
|
16
16
|
```powershell
|
|
17
|
-
dsh plugin --profile web add --workspace-root ./foggy-projects-deepseek-harness-plugin-0.4.0-beta.
|
|
17
|
+
dsh plugin --profile web add --workspace-root ./foggy-projects-deepseek-harness-plugin-0.4.0-beta.15.tgz
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
Restart `dsh web`, use the browser it opens (or the complete printed URL,
|
|
@@ -30,9 +30,13 @@ dsh plugin --profile web add --workspace-root @foggy-projects/deepseek-harness-p
|
|
|
30
30
|
For development, `FOGGY_ASSET_CACHE_DIRS` can contain platform-delimited verified
|
|
31
31
|
asset-cache directories. It is not required for a normal install.
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
The bundled onboarding Skill is intentionally development-first. Users may
|
|
34
|
+
provide a local datasource password in the conversation, a user-managed
|
|
35
|
+
connection file, an Agent environment variable, or a compatible Runtime
|
|
36
|
+
Console. Direct passwords are submitted to the already-running Runtime without
|
|
37
|
+
being copied into onboarding state or evidence. The Skill then drives schema
|
|
38
|
+
discovery, semantic drafting, local Bundle registration, and bounded query
|
|
39
|
+
verification without restarting Runtime.
|
|
36
40
|
|
|
37
41
|
## Native Skill and workspace contract
|
|
38
42
|
|
|
@@ -42,14 +46,12 @@ are available in every DSH workspace without copying or symlinking `.agents`.
|
|
|
42
46
|
The current session `cwd` remains the workspace boundary for semantic drafts and
|
|
43
47
|
evidence.
|
|
44
48
|
|
|
45
|
-
Opaque CLI profiles
|
|
46
|
-
`<dataRoot>/cli-profiles` directory
|
|
49
|
+
Opaque CLI profiles remain available for users who prefer them and default to
|
|
50
|
+
the private persistent `<dataRoot>/cli-profiles` directory, but they are not a
|
|
51
|
+
prerequisite for ordinary development. Composite onboarding commands are
|
|
47
52
|
idempotent: unchanged completed phases are resumed rather than re-adding a
|
|
48
|
-
datasource or re-registering a
|
|
49
|
-
|
|
50
|
-
persistent store. A completed profile can also be bound non-destructively to an
|
|
51
|
-
additional DSH workspace when its reviewed connection contract and published
|
|
52
|
-
semantic digest are unchanged.
|
|
53
|
+
datasource or re-registering a local Bundle. Settings detects legacy temporary
|
|
54
|
+
profiles and offers an explicit, validated migration into the persistent store.
|
|
53
55
|
|
|
54
56
|
Private Python, CLI, Launcher, the analysis Skill, install state, and Runtime
|
|
55
57
|
state live in the user-level Foggy component directories. The managed CLI is intentionally isolated
|
|
@@ -67,9 +69,17 @@ the managed Runtime directory. Runtime start monitors the Launcher PID during
|
|
|
67
69
|
process is verified with `wait-ready` and `capabilities` instead of being treated as a
|
|
68
70
|
failed second start.
|
|
69
71
|
|
|
70
|
-
|
|
71
|
-
`
|
|
72
|
-
a
|
|
72
|
+
The same settings tab owns a persistent local Runtime port. New installations default
|
|
73
|
+
to `18166`; users can choose another port while Runtime is stopped. Startup checks the
|
|
74
|
+
same wildcard binding used by the Java server, so a conflicting application or Windows
|
|
75
|
+
port proxy produces an immediate, high-visibility error instead of a readiness timeout.
|
|
76
|
+
The CLI and Skills resolve the resulting stable Runtime URL from managed state.
|
|
77
|
+
|
|
78
|
+
This beta remains a local dev/test integration. It does not automatically extend
|
|
79
|
+
local credentials or approvals into a formal environment. Production model
|
|
80
|
+
publication should use a separate manual or dedicated deployment workflow with
|
|
81
|
+
an explicit target, model Git commit/tag, credentials, verification, and
|
|
82
|
+
rollback plan.
|
|
73
83
|
|
|
74
84
|
See [`docs/PUBLIC-BETA-READINESS.md`](./docs/PUBLIC-BETA-READINESS.md) for the
|
|
75
85
|
tested public Beta scope, release gates, and stable-release blockers.
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
# Public beta readiness: 0.4.0-beta.
|
|
1
|
+
# Public beta readiness: 0.4.0-beta.15
|
|
2
2
|
|
|
3
3
|
Assessment date: 2026-09-04
|
|
4
4
|
|
|
5
5
|
## Verdict
|
|
6
6
|
|
|
7
|
-
`0.4.0-beta.
|
|
7
|
+
`0.4.0-beta.15` is suitable for a scoped public beta on Windows 10/11 x64 and
|
|
8
8
|
Linux/WSL2 x64. It is not a general-availability release.
|
|
9
9
|
|
|
10
10
|
The plugin package stays small and downloads large components only during
|
|
@@ -14,7 +14,7 @@ system PATH, registry, or a user's existing Python installation.
|
|
|
14
14
|
|
|
15
15
|
## Verified behavior
|
|
16
16
|
|
|
17
|
-
- The npm tarball is about
|
|
17
|
+
- The npm tarball is about 78 KB compressed and contains no Python or Java
|
|
18
18
|
runtime binary.
|
|
19
19
|
- Managed CPython assets are pinned by URL, byte size, and SHA256 for Windows,
|
|
20
20
|
Linux, and macOS on x64 and arm64.
|
|
@@ -26,8 +26,22 @@ system PATH, registry, or a user's existing Python installation.
|
|
|
26
26
|
- WSL2 reported the existing managed Python, CLI, Launcher, and analysis Skill
|
|
27
27
|
as ready; an isolated Windows profile correctly reported its fresh component
|
|
28
28
|
state and rejected the host's Java 12 as below the Java 17 prerequisite.
|
|
29
|
+
- Onboarding is development-first: a direct password or Agent environment
|
|
30
|
+
variable is submitted to the public datasource API of an already-running
|
|
31
|
+
Runtime. The password is excluded from onboarding state and evidence, and
|
|
32
|
+
datasource setup does not trigger Runtime restart. Opaque profiles remain an
|
|
33
|
+
optional advanced path.
|
|
34
|
+
- A real Launcher 0.1.18 smoke on Java 17 accepted a synthetic inline-password
|
|
35
|
+
datasource through the public Runtime API while retaining the same Java PID.
|
|
36
|
+
The isolated registry confirmed the documented local-development plaintext
|
|
37
|
+
persistence boundary; the test datasource was then removed and the temporary
|
|
38
|
+
Runtime stopped.
|
|
39
|
+
- A full isolated `onboard-datasource-run` then created, tested, bound, and
|
|
40
|
+
inspected a SQLite datasource containing one table. Runtime retained the same
|
|
41
|
+
PID, while the persisted onboarding state and command evidence contained no
|
|
42
|
+
copy of the synthetic password.
|
|
29
43
|
- The UI reported Python 3.12.13 as `Foggy private`, and CLI 0.1.23, Launcher
|
|
30
|
-
0.1.18, analysis Skill 0.1.17, and onboarding Skill 0.4.0-beta.
|
|
44
|
+
0.1.18, analysis Skill 0.1.17, and onboarding Skill 0.4.0-beta.15 as installed.
|
|
31
45
|
- Runtime startup now reports the current phase, elapsed/timeout seconds, and a
|
|
32
46
|
bounded progress value. A real Windows launch with Temurin 17 reached readiness
|
|
33
47
|
and passed capabilities in 18.9 seconds. A controlled post-launch Java exit was
|
|
@@ -37,12 +51,20 @@ system PATH, registry, or a user's existing Python installation.
|
|
|
37
51
|
error, including the generated local JSON path. Exported failures include
|
|
38
52
|
bounded sanitized Runtime log tails and reject log paths outside the managed
|
|
39
53
|
Runtime directory.
|
|
54
|
+
- Runtime port configuration is persisted below the private Foggy data root and
|
|
55
|
+
defaults to `18166`. The settings UI validates `1024–65535`, prevents changes
|
|
56
|
+
while Runtime is active, and shows port conflicts as an assertive, red bordered
|
|
57
|
+
alert. Preflight now checks `0.0.0.0` with exclusive Windows binding semantics,
|
|
58
|
+
matching the Java server closely enough to detect address-specific `portproxy`
|
|
59
|
+
conflicts that a loopback-only probe missed.
|
|
40
60
|
- Interrupted downloads resume with HTTP Range when the server supports it;
|
|
41
61
|
downloaded artifacts are not trusted until the complete SHA256 matches.
|
|
42
62
|
- Windows progress writes tolerate transient sharing violations without losing
|
|
43
63
|
the previous valid progress document or leaving temporary files behind.
|
|
44
|
-
-
|
|
45
|
-
|
|
64
|
+
- Current automated regression passes 20 Node tests and 20 Python tests. The
|
|
65
|
+
new datasource tests cover secret-free persisted state, direct Runtime API
|
|
66
|
+
submission, CLI bypass for inline development credentials, and output
|
|
67
|
+
redaction.
|
|
46
68
|
|
|
47
69
|
## Beta boundaries
|
|
48
70
|
|
|
@@ -57,6 +79,10 @@ system PATH, registry, or a user's existing Python installation.
|
|
|
57
79
|
- Runtime production authentication is outside this beta's default local
|
|
58
80
|
onboarding mode. The UI and doctor continue to report `productionReady=false`
|
|
59
81
|
for the development-only no-auth mode.
|
|
82
|
+
- Direct development passwords are stored by the current Runtime in its private
|
|
83
|
+
local datasource registry. Production publication is a separate manual or
|
|
84
|
+
future dedicated deployment workflow and should use the target environment's
|
|
85
|
+
credential controls.
|
|
60
86
|
|
|
61
87
|
## Release gate after publication
|
|
62
88
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Native Windows Beta acceptance
|
|
2
2
|
|
|
3
3
|
This runbook validates DeepSeek Harness `0.1.2-rc.1` with Foggy plugin
|
|
4
|
-
`0.4.0-beta.
|
|
4
|
+
`0.4.0-beta.15` on a clean 64-bit Windows 10 or Windows 11 machine. Use a local
|
|
5
5
|
directory that is not synchronized by OneDrive.
|
|
6
6
|
|
|
7
7
|
## Prerequisites
|
|
@@ -43,13 +43,18 @@ temporary local access material and do not paste it into chat or diagnostics.
|
|
|
43
43
|
Configure an LLM provider, select the `C:\FoggyAcceptance` workspace, then open
|
|
44
44
|
**Settings → Plugins → Foggy Data Analysis** and choose **Initialize Foggy**.
|
|
45
45
|
|
|
46
|
+
Before starting Runtime, confirm the configured port in **Runtime connection
|
|
47
|
+
settings**. A fresh profile defaults to `18166`. To use another port, enter an
|
|
48
|
+
integer from `1024` through `65535` and choose **Save port**. The value remains
|
|
49
|
+
stable across DSH restarts and cannot be changed while Runtime is running.
|
|
50
|
+
|
|
46
51
|
Expected component state:
|
|
47
52
|
|
|
48
53
|
- Python 3.12.13, source `Foggy private`;
|
|
49
54
|
- CLI 0.1.23;
|
|
50
55
|
- Launcher 0.1.18;
|
|
51
56
|
- analysis Skill 0.1.17;
|
|
52
|
-
- onboarding Skill 0.4.0-beta.
|
|
57
|
+
- onboarding Skill 0.4.0-beta.15;
|
|
53
58
|
- Java 17+ available;
|
|
54
59
|
- native DSH Skill registration available.
|
|
55
60
|
|
|
@@ -61,23 +66,32 @@ readiness. On failure, export diagnostics before retrying; the report includes
|
|
|
61
66
|
the startup phase, PID state, evidence directory, Runtime log locations, and
|
|
62
67
|
bounded sanitized log tails.
|
|
63
68
|
|
|
69
|
+
If the configured port is occupied, startup must fail during preflight instead
|
|
70
|
+
of waiting for the readiness deadline. The settings page displays a red bordered
|
|
71
|
+
alert naming the port conflict and tells the user to save another port or release
|
|
72
|
+
the conflicting application or Windows port proxy. Saving a free port and
|
|
73
|
+
starting again must clear the applicable conflict state.
|
|
74
|
+
|
|
64
75
|
## Database and semantic-layer acceptance
|
|
65
76
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
77
|
+
Use a development or test database. For this local acceptance run, connection
|
|
78
|
+
details and a password may be supplied directly in the prompt or in a local
|
|
79
|
+
connection JSON file. The onboarding wrapper must configure the datasource
|
|
80
|
+
online without stopping or restarting an already-running Runtime. It must not
|
|
81
|
+
copy the password into onboarding state, evidence, TM/QM files, or Git.
|
|
70
82
|
|
|
71
83
|
Use a natural prompt such as:
|
|
72
84
|
|
|
73
85
|
```text
|
|
74
|
-
请帮我用 Foggy
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
86
|
+
请帮我用 Foggy 分析这个测试数据库:地址、数据库名、账号和密码分别是
|
|
87
|
+
<连接信息>。直接创建开发数据源并绑定一个合适的 namespace,不要重启
|
|
88
|
+
Runtime。读取表结构后,在当前工作目录创建订单语义模型,包含订单数量、
|
|
89
|
+
状态和开单时间,调试到模型通过,并执行一个不超过 20 行的只读查询。
|
|
78
90
|
```
|
|
79
91
|
|
|
80
|
-
Pass only when Runtime readiness and capabilities succeed, datasource
|
|
81
|
-
can be discovered, TM/QM models validate and
|
|
82
|
-
|
|
83
|
-
|
|
92
|
+
Pass only when Runtime readiness and capabilities succeed, datasource setup
|
|
93
|
+
does not restart Runtime, metadata can be discovered, TM/QM models validate and
|
|
94
|
+
become effective in the local Runtime, the bounded read-only query succeeds,
|
|
95
|
+
diagnostics contain no credentials, and stopping Runtime frees the configured
|
|
96
|
+
port (default `18166`). Recommend Git management for the completed model files;
|
|
97
|
+
do not treat this acceptance run as production publication.
|
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: [
|
|
37
|
+
...[
|
|
14
38
|
'status', 'plan', 'initialize', 'repair', 'repairPython', 'repairCli', 'repairLauncher', 'repairAnalysisSkill',
|
|
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 = {
|
|
@@ -108,6 +130,21 @@ window.__ModuleLoader__.load({
|
|
|
108
130
|
stepSemanticPublished: '发布',
|
|
109
131
|
stepSemanticVerified: '首次查询',
|
|
110
132
|
runtimeUrl: 'Runtime 地址',
|
|
133
|
+
runtimeSettingsTitle: 'Runtime 连接设置',
|
|
134
|
+
runtimePort: 'Runtime 端口',
|
|
135
|
+
runtimePortHint: '端口会持久保存,CLI 与 Skill 将使用同一个 Runtime 地址。',
|
|
136
|
+
configuredRuntimeUrl: '下次启动地址',
|
|
137
|
+
saveRuntimePort: '保存端口',
|
|
138
|
+
runtimePortSaved: 'Runtime 端口已保存',
|
|
139
|
+
stopToChangePort: 'Runtime 运行时不能修改端口,请先停止 Runtime。',
|
|
140
|
+
errorTitle: 'Foggy 操作失败',
|
|
141
|
+
portUnavailableTitle: 'Runtime 端口被占用',
|
|
142
|
+
portUnavailableMessage: '已被其他程序或 Windows 端口代理占用,Runtime 无法启动。',
|
|
143
|
+
portUnavailableHelp: '请更换端口后保存并重新启动;也可以先释放占用该端口的程序或 Windows 端口代理。',
|
|
144
|
+
portInvalidTitle: '端口配置无效',
|
|
145
|
+
portInvalidMessage: '当前值无法保存为 Runtime 端口。',
|
|
146
|
+
portInvalidHelp: '请输入 1024 到 65535 之间的整数端口。',
|
|
147
|
+
settingsInvalidTitle: 'Runtime 设置文件异常',
|
|
111
148
|
nextTitle: '后续配置',
|
|
112
149
|
nextCopy: 'Skills 已通过 DeepSeek Harness 原生注册表提供给所有工作区;每个会话直接使用自己的工作目录。Runtime 启动成功后,将继续进入数据库连接与语义层向导。',
|
|
113
150
|
beta: 'Beta',
|
|
@@ -192,6 +229,21 @@ window.__ModuleLoader__.load({
|
|
|
192
229
|
stepSemanticPublished: 'Publish',
|
|
193
230
|
stepSemanticVerified: 'First query',
|
|
194
231
|
runtimeUrl: 'Runtime URL',
|
|
232
|
+
runtimeSettingsTitle: 'Runtime connection settings',
|
|
233
|
+
runtimePort: 'Runtime port',
|
|
234
|
+
runtimePortHint: 'The port is persisted so the CLI and Skills use one stable Runtime URL.',
|
|
235
|
+
configuredRuntimeUrl: 'Next startup URL',
|
|
236
|
+
saveRuntimePort: 'Save port',
|
|
237
|
+
runtimePortSaved: 'Runtime port saved',
|
|
238
|
+
stopToChangePort: 'Stop Runtime before changing its port.',
|
|
239
|
+
errorTitle: 'Foggy operation failed',
|
|
240
|
+
portUnavailableTitle: 'Runtime port is already in use',
|
|
241
|
+
portUnavailableMessage: 'is already used by another application or Windows port proxy, so Runtime cannot start.',
|
|
242
|
+
portUnavailableHelp: 'Save a different port and start again, or release the application or Windows port proxy using this port.',
|
|
243
|
+
portInvalidTitle: 'Invalid port setting',
|
|
244
|
+
portInvalidMessage: 'The current value cannot be saved as the Runtime port.',
|
|
245
|
+
portInvalidHelp: 'Enter an integer port between 1024 and 65535.',
|
|
246
|
+
settingsInvalidTitle: 'Runtime settings file is invalid',
|
|
195
247
|
nextTitle: 'Next configuration',
|
|
196
248
|
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.',
|
|
197
249
|
beta: 'Beta',
|
|
@@ -206,8 +258,10 @@ window.__ModuleLoader__.load({
|
|
|
206
258
|
.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}
|
|
207
259
|
.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}
|
|
208
260
|
.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}
|
|
261
|
+
.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)}
|
|
262
|
+
.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}
|
|
209
263
|
.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}
|
|
210
|
-
@media (width<=680px){.foggy-grid{grid-template-columns:minmax(0,1fr)}}
|
|
264
|
+
@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%}}
|
|
211
265
|
`
|
|
212
266
|
|
|
213
267
|
if (typeof document !== 'undefined' && !document.querySelector('style[data-foggy-settings]')) {
|
|
@@ -347,18 +401,44 @@ window.__ModuleLoader__.load({
|
|
|
347
401
|
] })
|
|
348
402
|
}
|
|
349
403
|
|
|
404
|
+
function errorPanel(title, message, help) {
|
|
405
|
+
if (!message) return null
|
|
406
|
+
return jsxs('div', {
|
|
407
|
+
className: 'foggy-error-panel',
|
|
408
|
+
role: 'alert',
|
|
409
|
+
'aria-live': 'assertive',
|
|
410
|
+
children: [
|
|
411
|
+
jsxs('div', { className: 'foggy-error-panel-head', children: [
|
|
412
|
+
jsx('span', { className: 'foggy-error-icon', 'aria-hidden': true, children: '!' }),
|
|
413
|
+
jsx('strong', { children: title }),
|
|
414
|
+
] }),
|
|
415
|
+
jsx('p', { children: message }),
|
|
416
|
+
help ? jsx('p', { className: 'foggy-error-help', children: help }) : null,
|
|
417
|
+
],
|
|
418
|
+
})
|
|
419
|
+
}
|
|
420
|
+
|
|
350
421
|
function FoggySettingsTab({ api, t }) {
|
|
351
|
-
const [view, setView] = useState({ phase: 'loading', status: null, message: '', error: false })
|
|
422
|
+
const [view, setView] = useState({ phase: 'loading', status: null, message: '', error: false, errorCode: null })
|
|
423
|
+
const [portDraft, setPortDraft] = useState('')
|
|
424
|
+
const [portDirty, setPortDirty] = useState(false)
|
|
352
425
|
|
|
353
426
|
const refresh = useCallback(async () => {
|
|
354
427
|
setView((current) => ({ ...current, phase: current.status ? 'ready' : 'loading', error: false }))
|
|
355
428
|
try {
|
|
356
429
|
const status = unwrap(await api.status(), 'status')
|
|
357
|
-
setView((current) => ({
|
|
430
|
+
setView((current) => ({
|
|
431
|
+
...current,
|
|
432
|
+
phase: 'ready',
|
|
433
|
+
status,
|
|
434
|
+
error: false,
|
|
435
|
+
message: current.message === t('accepted') && status.operation?.state !== 'running' ? '' : current.message,
|
|
436
|
+
}))
|
|
437
|
+
if (!portDirty) setPortDraft(String(status.runtimeSettings?.port ?? 18166))
|
|
358
438
|
} catch (error) {
|
|
359
439
|
setView((current) => ({ ...current, phase: current.status ? 'ready' : 'error', message: String(error.message || error), error: true }))
|
|
360
440
|
}
|
|
361
|
-
}, [api])
|
|
441
|
+
}, [api, portDirty, t])
|
|
362
442
|
|
|
363
443
|
useEffect(() => { refresh() }, [refresh])
|
|
364
444
|
useEffect(() => {
|
|
@@ -368,18 +448,40 @@ window.__ModuleLoader__.load({
|
|
|
368
448
|
}, [refresh, view.status?.operation?.state])
|
|
369
449
|
|
|
370
450
|
const run = async (method) => {
|
|
371
|
-
setView((current) => ({ ...current, message: t('working'), error: false }))
|
|
451
|
+
setView((current) => ({ ...current, message: t('working'), error: false, errorCode: null }))
|
|
372
452
|
try {
|
|
373
453
|
const result = unwrap(await api[method](), method)
|
|
374
454
|
const message = result.accepted
|
|
375
455
|
? t('accepted')
|
|
376
456
|
: result.path
|
|
377
457
|
? `${t('diagnosticsSaved')}: ${result.path}`
|
|
378
|
-
: ''
|
|
379
|
-
setView((current) => ({ ...current, message, error: result.success === false }))
|
|
458
|
+
: result.error?.message || ''
|
|
459
|
+
setView((current) => ({ ...current, message, error: result.success === false, errorCode: result.error?.code || null }))
|
|
380
460
|
await refresh()
|
|
381
461
|
} catch (error) {
|
|
382
|
-
setView((current) => ({ ...current, message: `${t('actionFailed')}: ${String(error.message || error)}`, error: true }))
|
|
462
|
+
setView((current) => ({ ...current, message: `${t('actionFailed')}: ${String(error.message || error)}`, error: true, errorCode: 'REMOTE_ERROR' }))
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
const saveRuntimePort = async () => {
|
|
467
|
+
const port = Number(portDraft)
|
|
468
|
+
if (!Number.isInteger(port) || port < 1024 || port > 65535) {
|
|
469
|
+
setView((current) => ({ ...current, message: t('portInvalidMessage'), error: true, errorCode: 'RUNTIME_PORT_INVALID' }))
|
|
470
|
+
return
|
|
471
|
+
}
|
|
472
|
+
setView((current) => ({ ...current, message: t('working'), error: false, errorCode: null }))
|
|
473
|
+
try {
|
|
474
|
+
const result = unwrap(await api.saveRuntimeSettings({ port }), 'saveRuntimeSettings')
|
|
475
|
+
if (result.success === false) {
|
|
476
|
+
setView((current) => ({ ...current, message: result.error?.message || t('actionFailed'), error: true, errorCode: result.error?.code || null }))
|
|
477
|
+
return
|
|
478
|
+
}
|
|
479
|
+
setPortDirty(false)
|
|
480
|
+
setPortDraft(String(result.settings.port))
|
|
481
|
+
setView((current) => ({ ...current, message: `${t('runtimePortSaved')}: ${result.settings.runtimeUrl}`, error: false, errorCode: null }))
|
|
482
|
+
await refresh()
|
|
483
|
+
} catch (error) {
|
|
484
|
+
setView((current) => ({ ...current, message: `${t('actionFailed')}: ${String(error.message || error)}`, error: true, errorCode: 'REMOTE_ERROR' }))
|
|
383
485
|
}
|
|
384
486
|
}
|
|
385
487
|
|
|
@@ -391,9 +493,38 @@ window.__ModuleLoader__.load({
|
|
|
391
493
|
|
|
392
494
|
const status = view.status
|
|
393
495
|
const busy = status.operation?.state === 'running'
|
|
496
|
+
const portNumber = Number(portDraft)
|
|
497
|
+
const portValid = Number.isInteger(portNumber) && portNumber >= 1024 && portNumber <= 65535
|
|
394
498
|
const operationError = status.operation?.state === 'failed'
|
|
395
499
|
? status.operation.error || status.operation.result?.error?.message || t('actionFailed')
|
|
396
500
|
: ''
|
|
501
|
+
const operationErrorCode = status.operation?.state === 'failed' ? status.operation.result?.error?.code || null : null
|
|
502
|
+
const settingsError = status.runtimeSettings?.valid === false
|
|
503
|
+
? status.runtimeSettings.error || t('settingsInvalidTitle')
|
|
504
|
+
: ''
|
|
505
|
+
const portConflict = status.runtimeSettings?.conflictApplies ? status.runtimeSettings.lastConflict : null
|
|
506
|
+
const validationError = portDirty && !portValid ? t('portInvalidMessage') : ''
|
|
507
|
+
const rawAlertMessage = validationError || (view.error ? view.message : '') || operationError || settingsError || portConflict?.message || ''
|
|
508
|
+
const alertCode = validationError
|
|
509
|
+
? 'RUNTIME_PORT_INVALID'
|
|
510
|
+
: view.error
|
|
511
|
+
? view.errorCode
|
|
512
|
+
: operationErrorCode || (settingsError ? 'RUNTIME_SETTINGS_INVALID' : portConflict ? 'RUNTIME_PORT_UNAVAILABLE' : null)
|
|
513
|
+
const alertTitle = alertCode === 'RUNTIME_PORT_UNAVAILABLE'
|
|
514
|
+
? t('portUnavailableTitle')
|
|
515
|
+
: alertCode === 'RUNTIME_PORT_INVALID'
|
|
516
|
+
? t('portInvalidTitle')
|
|
517
|
+
: alertCode === 'RUNTIME_SETTINGS_INVALID'
|
|
518
|
+
? t('settingsInvalidTitle')
|
|
519
|
+
: t('errorTitle')
|
|
520
|
+
const alertMessage = alertCode === 'RUNTIME_PORT_UNAVAILABLE'
|
|
521
|
+
? `${t('runtimePort')} ${portConflict?.port ?? status.runtimeSettings?.port ?? portNumber} ${t('portUnavailableMessage')}`
|
|
522
|
+
: rawAlertMessage
|
|
523
|
+
const alertHelp = alertCode === 'RUNTIME_PORT_UNAVAILABLE'
|
|
524
|
+
? t('portUnavailableHelp')
|
|
525
|
+
: alertCode === 'RUNTIME_PORT_INVALID'
|
|
526
|
+
? t('portInvalidHelp')
|
|
527
|
+
: ''
|
|
397
528
|
const progress = busy ? progressPanel(status.operation?.progress, t) : null
|
|
398
529
|
const stateLabels = { 'not-installed': 'notInstalled', degraded: 'degraded', ready: 'ready', running: 'running' }
|
|
399
530
|
return jsxs('section', { className: 'foggy-settings', children: [
|
|
@@ -412,10 +543,46 @@ window.__ModuleLoader__.load({
|
|
|
412
543
|
status.running ? jsx('button', { className: 'foggy-button', type: 'button', disabled: busy, onClick: () => run('runtimeStop'), children: t('stop') }) : null,
|
|
413
544
|
] }),
|
|
414
545
|
] }),
|
|
546
|
+
errorPanel(alertTitle, alertMessage, alertHelp),
|
|
415
547
|
progress,
|
|
416
548
|
busy ? jsx('p', { className: 'foggy-message', children: t('working') }) : null,
|
|
417
|
-
operationError ? jsx('p', { className: 'foggy-message',
|
|
418
|
-
|
|
549
|
+
!busy && view.message && !view.error && !operationError ? jsx('p', { className: 'foggy-message', children: view.message }) : null,
|
|
550
|
+
jsxs('div', { className: 'foggy-runtime-settings', children: [
|
|
551
|
+
jsx('h4', { children: t('runtimeSettingsTitle') }),
|
|
552
|
+
jsxs('div', { className: 'foggy-runtime-settings-row', children: [
|
|
553
|
+
jsxs('div', { className: 'foggy-field', children: [
|
|
554
|
+
jsx('label', { htmlFor: 'foggy-runtime-port', children: t('runtimePort') }),
|
|
555
|
+
jsx('input', {
|
|
556
|
+
id: 'foggy-runtime-port',
|
|
557
|
+
className: 'foggy-input',
|
|
558
|
+
type: 'number',
|
|
559
|
+
min: 1024,
|
|
560
|
+
max: 65535,
|
|
561
|
+
step: 1,
|
|
562
|
+
value: portDraft,
|
|
563
|
+
disabled: busy || status.running,
|
|
564
|
+
'aria-invalid': portDirty && !portValid ? 'true' : undefined,
|
|
565
|
+
onChange: (event) => {
|
|
566
|
+
setPortDraft(event.target.value)
|
|
567
|
+
setPortDirty(true)
|
|
568
|
+
setView((current) => ({ ...current, message: '', error: false, errorCode: null }))
|
|
569
|
+
},
|
|
570
|
+
}),
|
|
571
|
+
] }),
|
|
572
|
+
jsxs('div', { className: 'foggy-setting-meta', children: [
|
|
573
|
+
jsx('span', { children: t('runtimePortHint') }),
|
|
574
|
+
jsx('code', { children: `${t('configuredRuntimeUrl')}: http://127.0.0.1:${portValid ? portNumber : '—'}` }),
|
|
575
|
+
status.running ? jsx('span', { children: t('stopToChangePort') }) : null,
|
|
576
|
+
] }),
|
|
577
|
+
jsx('button', {
|
|
578
|
+
className: 'foggy-button foggy-button-primary',
|
|
579
|
+
type: 'button',
|
|
580
|
+
disabled: busy || status.running || !portDirty || !portValid,
|
|
581
|
+
onClick: saveRuntimePort,
|
|
582
|
+
children: t('saveRuntimePort'),
|
|
583
|
+
}),
|
|
584
|
+
] }),
|
|
585
|
+
] }),
|
|
419
586
|
jsxs('div', { className: 'foggy-grid', children: [
|
|
420
587
|
componentCard(t('python'), status.components.python, 'tool', t),
|
|
421
588
|
componentCard(t('java'), status.components.java, 'tool', t),
|