@foggy-projects/deepseek-harness-plugin 0.4.2-rc.1 → 0.4.2-rc.3
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 -7
- package/docs/RELEASE-CANDIDATE-0.4.2-DSH-0.1.7-RC.2.md +50 -0
- package/docs/RELEASE-CANDIDATE-0.4.2-DSH-0.1.7-RC.3.md +34 -0
- package/docs/WINDOWS-BETA-ACCEPTANCE.md +17 -9
- package/experience/linux/README.md +2 -2
- package/experience/linux/prepare.sh +1 -1
- package/lib/client.js +111 -8
- package/lib/index.js +10 -1
- package/lib/query-tool.js +104 -0
- package/lib/remote-descriptor.js +11 -0
- package/lib/viewer-preview.js +27 -0
- package/package.json +9 -4
- package/skills/foggy-deepseek-onboarding/SKILL.md +9 -2
- package/skills/foggy-deepseek-onboarding/assets/versions.json +16 -16
- package/skills/foggy-deepseek-onboarding/scripts/onboarding.py +51 -5
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ are downloaded only when the user selects **Initialize and start** (or the
|
|
|
7
7
|
equivalent component action). Existing installations are not silently changed
|
|
8
8
|
when the plugin package is upgraded.
|
|
9
9
|
|
|
10
|
-
The `0.4.2-rc.
|
|
10
|
+
The `0.4.2-rc.3` compatibility candidate targets DeepSeek Harness `0.1.7-rc.2`
|
|
11
11
|
and requires a system Node.js `>=24.0.0`. Foggy's Java Launcher uses a system
|
|
12
12
|
Java 17+. Foggy manages its own pinned Python 3.12 runtime
|
|
13
13
|
inside the per-user component directory; it does not require a system Python,
|
|
@@ -17,22 +17,23 @@ modify `PATH`, or register Python globally. Advanced users may explicitly set
|
|
|
17
17
|
## Local release-candidate installation
|
|
18
18
|
|
|
19
19
|
```powershell
|
|
20
|
-
dsh plugin --profile web add --workspace-root ./foggy-projects-deepseek-harness-plugin-0.4.2-rc.
|
|
20
|
+
dsh plugin --profile web add --workspace-root ./foggy-projects-deepseek-harness-plugin-0.4.2-rc.3.tgz
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
Restart `dsh web`, use the browser it opens (or the complete printed URL,
|
|
24
24
|
including `?token=...`), open Settings → Plugins → Foggy Data Analysis, and
|
|
25
25
|
initialize the components from there. Do not share the launch-token URL.
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
Install the exact DSH 0.1.7-rc.2 compatibility candidate
|
|
28
28
|
so the stable `beta` channel is not changed:
|
|
29
29
|
|
|
30
30
|
```powershell
|
|
31
|
-
dsh plugin --profile web add --workspace-root @foggy-projects/deepseek-harness-plugin@0.4.2-rc.
|
|
31
|
+
dsh plugin --profile web add --workspace-root @foggy-projects/deepseek-harness-plugin@0.4.2-rc.3
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
The
|
|
35
|
-
|
|
34
|
+
The `dsh017` npm dist-tag points to the most recently published DSH 0.1.7
|
|
35
|
+
candidate (`0.4.2-rc.3` at this release). Check the resolved version before
|
|
36
|
+
using this moving tag:
|
|
36
37
|
|
|
37
38
|
```powershell
|
|
38
39
|
dsh plugin --profile web add --workspace-root @foggy-projects/deepseek-harness-plugin@dsh017
|
|
@@ -91,6 +92,11 @@ The Bundle registers `foggy-deepseek-onboarding` and the downloaded
|
|
|
91
92
|
are available in every DSH workspace without copying or symlinking `.agents`.
|
|
92
93
|
The current session `cwd` remains the workspace boundary for semantic drafts and
|
|
93
94
|
evidence.
|
|
95
|
+
An existing Harness session keeps its original workspace even if another workspace
|
|
96
|
+
becomes the default. Create a new session in the selected workspace when testing
|
|
97
|
+
model-file isolation; an isolated `DSH_HOME` alone does not change a session's
|
|
98
|
+
`cwd`. The `--workspace-root` option in the plugin install command is a pnpm
|
|
99
|
+
installation option, not a Harness workspace selector.
|
|
94
100
|
|
|
95
101
|
Opaque CLI profiles remain available for users who prefer them and default to
|
|
96
102
|
the private persistent `<dataRoot>/cli-profiles` directory, but they are not a
|
|
@@ -124,6 +130,15 @@ same wildcard binding used by the Java server, so a conflicting application or W
|
|
|
124
130
|
port proxy produces an immediate, high-visibility error instead of a readiness timeout.
|
|
125
131
|
The CLI and Skills resolve the resulting stable Runtime URL from managed state.
|
|
126
132
|
|
|
133
|
+
For a successful standard QueryModel DSL call made through the native
|
|
134
|
+
`foggy_query` tool, Harness shows a query card with the model, namespace,
|
|
135
|
+
row count, DSL, and execution result. Its **Open DataViewer** action creates
|
|
136
|
+
a local preview URL only when clicked; the query itself does not create one.
|
|
137
|
+
The preview re-executes the DSL against the current model and data rather than
|
|
138
|
+
freezing the original rows. In the lite Runtime the preview cache is bounded,
|
|
139
|
+
process-local, and lost on restart. CTE calls are not included in this first
|
|
140
|
+
DataViewer integration.
|
|
141
|
+
|
|
127
142
|
This beta remains a local dev/test integration. It does not automatically extend
|
|
128
143
|
local credentials or approvals into a formal environment. Production model
|
|
129
144
|
publication should use a separate manual or dedicated deployment workflow with
|
|
@@ -133,7 +148,9 @@ rollback plan.
|
|
|
133
148
|
See [`docs/PUBLIC-BETA-READINESS.md`](./docs/PUBLIC-BETA-READINESS.md) for the
|
|
134
149
|
tested public Beta scope, release gates, and stable-release blockers.
|
|
135
150
|
The DSH 0.1.7-rc.2 compatibility candidate is documented in
|
|
136
|
-
[`docs/RELEASE-CANDIDATE-0.4.2-DSH-0.1.7-RC.
|
|
151
|
+
[`docs/RELEASE-CANDIDATE-0.4.2-DSH-0.1.7-RC.3.md`](./docs/RELEASE-CANDIDATE-0.4.2-DSH-0.1.7-RC.3.md).
|
|
152
|
+
The previous DSH 0.1.7-rc.2 candidate is documented in
|
|
153
|
+
[`docs/RELEASE-CANDIDATE-0.4.2-DSH-0.1.7-RC.2.md`](./docs/RELEASE-CANDIDATE-0.4.2-DSH-0.1.7-RC.2.md).
|
|
137
154
|
The earlier DSH 0.1.5-rc.2 candidate is documented in
|
|
138
155
|
[`docs/RELEASE-CANDIDATE-0.4.1-DSH-0.1.5-RC.1.md`](./docs/RELEASE-CANDIDATE-0.4.1-DSH-0.1.5-RC.1.md).
|
|
139
156
|
Native Windows acceptance instructions are in
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Foggy DeepSeek Harness plugin 0.4.2-rc.2
|
|
2
|
+
|
|
3
|
+
This pre-release targets DeepSeek Harness `0.1.7-rc.2` with system Node.js
|
|
4
|
+
`>=24.0.0` and Java 17+. It stays on the dedicated `dsh017` npm channel;
|
|
5
|
+
the existing `beta`, `rc`, and `dsh015` channels are unchanged.
|
|
6
|
+
|
|
7
|
+
## Changes since 0.4.2-rc.1
|
|
8
|
+
|
|
9
|
+
- Publish each polled operation state to the settings UI so initialization and
|
|
10
|
+
Runtime startup progress remain visible while the operation runs. Known
|
|
11
|
+
progress phases now use the selected UI language instead of raw English
|
|
12
|
+
backend messages.
|
|
13
|
+
- Use the managed CLI's `wait-ready` response as Doctor's Runtime health check.
|
|
14
|
+
Process inspection remains diagnostic context, so an unavailable Windows
|
|
15
|
+
process query does not incorrectly label a healthy Runtime as stale.
|
|
16
|
+
- Clarify workspace behavior: an existing DSH session retains its original
|
|
17
|
+
`cwd` when the default workspace changes. Create a new session in the chosen
|
|
18
|
+
workspace to isolate new semantic-model files.
|
|
19
|
+
|
|
20
|
+
## Acceptance
|
|
21
|
+
|
|
22
|
+
- On a fresh Windows component/data root, the settings page visibly showed
|
|
23
|
+
initialization progress during the private Python download (23%, step 2/7).
|
|
24
|
+
CLI `0.1.23`, Launcher `0.1.21`, and analysis/query Skills `0.1.18` installed.
|
|
25
|
+
- The default Runtime port `18166` was occupied by an earlier acceptance
|
|
26
|
+
Runtime. The new profile failed promptly at port preflight and displayed an
|
|
27
|
+
explicit red port-conflict alert. Saving port `18167` in the isolated data
|
|
28
|
+
root and starting the managed Launcher succeeded. Strict Doctor passed all
|
|
29
|
+
eight checks; CLI `wait-ready` and `capabilities` reported a healthy Java
|
|
30
|
+
Runtime at `http://127.0.0.1:18167`.
|
|
31
|
+
- A Harness chat created a separate TM/QM and a bounded 2024 monthly paid
|
|
32
|
+
amount query against the local demo `fact_order` table. Runtime model
|
|
33
|
+
describe, query validate, and query execute agreed with the four rows shown
|
|
34
|
+
in the chat. This remains development/test data, not production deployment.
|
|
35
|
+
- The candidate tarball was installed over the local `0.4.2-rc.1` test profile.
|
|
36
|
+
pnpm's lockfile policy passed; the installed client matched the packed source,
|
|
37
|
+
DSH Web restarted without plugin activation errors, and its authenticated
|
|
38
|
+
page returned HTTP 200. The existing component install state remained intact,
|
|
39
|
+
with Runtime still healthy on port `18167` and strict Doctor again passing
|
|
40
|
+
all eight checks.
|
|
41
|
+
|
|
42
|
+
Install the exact version with:
|
|
43
|
+
|
|
44
|
+
```powershell
|
|
45
|
+
dsh plugin --profile web add --workspace-root @foggy-projects/deepseek-harness-plugin@0.4.2-rc.2
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Publish this pre-release under the `dsh017` dist-tag only, leaving other npm
|
|
49
|
+
channels unchanged. The Linux `experience/linux/prepare.sh` entry is pinned to
|
|
50
|
+
the matching Git tag and becomes usable once that tag is published.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Foggy DeepSeek Harness plugin 0.4.2-rc.3
|
|
2
|
+
|
|
3
|
+
This pre-release targets DeepSeek Harness `0.1.7-rc.2` on the `dsh017` npm
|
|
4
|
+
channel. It keeps the `beta`, `rc`, and `dsh015` channels unchanged.
|
|
5
|
+
|
|
6
|
+
## Changes since 0.4.2-rc.2
|
|
7
|
+
|
|
8
|
+
- Add a native `foggy_query` tool for bounded, read-only standard QueryModel DSL
|
|
9
|
+
validation and execution. Successful calls render a Harness card with the
|
|
10
|
+
model, namespace, row count, DSL, SQL, and result.
|
|
11
|
+
- Create a DataViewer preview URL only when the card's **Open DataViewer**
|
|
12
|
+
action is clicked. Links are short-lived local development previews, not
|
|
13
|
+
frozen result snapshots. An expired link can be regenerated from the card.
|
|
14
|
+
- Update the managed Launcher to `0.1.22`. Its lite profile serves the existing
|
|
15
|
+
DataViewer UI using a bounded in-memory cache without MongoDB, and its start
|
|
16
|
+
scripts bind to loopback. Runtime restarts invalidate preview links.
|
|
17
|
+
- Do not claim CTE preview support in this release. Existing CTE execution
|
|
18
|
+
remains available through its prior CLI path without a DataViewer card.
|
|
19
|
+
|
|
20
|
+
## Acceptance scope
|
|
21
|
+
|
|
22
|
+
- Plugin checks: `npm run check`, Node tests, Python onboarding tests, and an
|
|
23
|
+
isolated DSH `0.1.7-rc.2` web boot with the Foggy settings tab visible.
|
|
24
|
+
- Launcher: formal release gate, default and Analytics Console smokes, six
|
|
25
|
+
GitHub assets, and SHA-256 verification.
|
|
26
|
+
- End-to-end DSL: an isolated Java Runtime with SQLite demo data validates and
|
|
27
|
+
executes a QueryModel query. A click-generated DataViewer link shows actual
|
|
28
|
+
columns and six rows in the browser.
|
|
29
|
+
|
|
30
|
+
Install the exact version with:
|
|
31
|
+
|
|
32
|
+
```powershell
|
|
33
|
+
dsh plugin --profile web add --workspace-root @foggy-projects/deepseek-harness-plugin@0.4.2-rc.3
|
|
34
|
+
```
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# Native Windows Beta acceptance
|
|
2
2
|
|
|
3
|
-
This runbook validates DeepSeek Harness `0.1.
|
|
4
|
-
`0.4.
|
|
3
|
+
This runbook validates DeepSeek Harness `0.1.7-rc.2` with Foggy plugin
|
|
4
|
+
`0.4.2-rc.3` 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
|
|
8
8
|
|
|
9
|
-
Install a system Node.js
|
|
9
|
+
Install a system Node.js `>=24.0.0` and a system Java 17+
|
|
10
10
|
JRE/JDK. Windows must also provide `tar` (included in supported Windows 10/11
|
|
11
11
|
versions). A system Python is neither required nor used by default.
|
|
12
12
|
|
|
@@ -27,11 +27,11 @@ and is not registered as a system Python.
|
|
|
27
27
|
New-Item -ItemType Directory -Force C:\FoggyAcceptance | Out-Null
|
|
28
28
|
Set-Location C:\FoggyAcceptance
|
|
29
29
|
|
|
30
|
-
npx --yes --package=@deepseek-ai/dsh@0.1.
|
|
30
|
+
npx --yes --package=@deepseek-ai/dsh@0.1.7-rc.2 --package=pnpm@11.7.0 `
|
|
31
31
|
dsh plugin --profile web add --workspace-root `
|
|
32
|
-
"@foggy-projects/deepseek-harness-plugin@
|
|
32
|
+
"@foggy-projects/deepseek-harness-plugin@0.4.2-rc.3"
|
|
33
33
|
|
|
34
|
-
npx --yes --package=@deepseek-ai/dsh@0.1.
|
|
34
|
+
npx --yes --package=@deepseek-ai/dsh@0.1.7-rc.2 --package=pnpm@11.7.0 dsh web
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
If an immediate upgrade reports
|
|
@@ -49,7 +49,13 @@ token has established its local authentication cookie. Treat the launch URL as
|
|
|
49
49
|
temporary local access material and do not paste it into chat or diagnostics.
|
|
50
50
|
|
|
51
51
|
Configure an LLM provider, select the `C:\FoggyAcceptance` workspace, then open
|
|
52
|
-
**Settings →
|
|
52
|
+
**Settings → Built-in plugins → Foggy Data Analysis** and choose
|
|
53
|
+
**Initialize and start**. The settings page displays the current phase and
|
|
54
|
+
step count while initialization runs. If an existing chat was opened before
|
|
55
|
+
selecting this workspace, create a new chat in the selected workspace: changing
|
|
56
|
+
the default workspace does not change an existing chat's working directory.
|
|
57
|
+
`--workspace-root` in the install command is a pnpm resolution option, not a
|
|
58
|
+
DSH workspace selector.
|
|
53
59
|
|
|
54
60
|
Before starting Runtime, confirm the configured port in **Runtime connection
|
|
55
61
|
settings**. A fresh profile defaults to `18166`. To use another port, enter an
|
|
@@ -60,15 +66,17 @@ Expected component state:
|
|
|
60
66
|
|
|
61
67
|
- Python 3.12.13, source `Foggy private`;
|
|
62
68
|
- CLI 0.1.23;
|
|
63
|
-
- Launcher 0.1.
|
|
69
|
+
- Launcher 0.1.22;
|
|
64
70
|
- analysis/query Skills 0.1.18;
|
|
65
|
-
- onboarding Skill 0.4.
|
|
71
|
+
- onboarding Skill 0.4.2-rc.3;
|
|
66
72
|
- Java 17+ available;
|
|
67
73
|
- native DSH Skill registration available.
|
|
68
74
|
|
|
69
75
|
Runtime startup shows its current phase, elapsed time, and the 180-second
|
|
70
76
|
readiness deadline. Typical startup is 15–30 seconds on the validated WSL2 host
|
|
71
77
|
and may take up to 60 seconds on Windows with cold JVM or antivirus scanning.
|
|
78
|
+
Initialization downloads a private Python and other managed components and can
|
|
79
|
+
take longer than Runtime startup; follow the live phase/step display.
|
|
72
80
|
The plugin monitors the Launcher PID and fails promptly when Java exits before
|
|
73
81
|
readiness. On failure, export diagnostics before retrying; the report includes
|
|
74
82
|
the startup phase, PID state, evidence directory, Runtime log locations, and
|
|
@@ -33,8 +33,8 @@ Start the prepared profile with:
|
|
|
33
33
|
~/.local/share/foggy-deepseek-harness-experience/run.sh
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
Open the URL printed by DSH, then choose **Settings →
|
|
37
|
-
**Initialize
|
|
36
|
+
Open the URL printed by DSH, then choose **Settings → Built-in plugins → Foggy Data Analysis** and select
|
|
37
|
+
**Initialize and start**. Private Python, the CLI, Launcher, and analysis Skill are downloaded from their
|
|
38
38
|
pinned public Releases and verified before installation. A system Python is not required.
|
|
39
39
|
|
|
40
40
|
Provider configuration remains a separate DSH setup step. Store API keys in a private environment
|
package/lib/client.js
CHANGED
|
@@ -17,6 +17,15 @@ window.__ModuleLoader__.load({
|
|
|
17
17
|
return { port }
|
|
18
18
|
},
|
|
19
19
|
}
|
|
20
|
+
const viewerPreviewInputSchema = {
|
|
21
|
+
parse: (value) => {
|
|
22
|
+
if (!value || typeof value.model !== 'string' || typeof value.namespace !== 'string'
|
|
23
|
+
|| !value.payload || typeof value.payload !== 'object' || Array.isArray(value.payload)) {
|
|
24
|
+
throw new Error('Invalid DataViewer preview request')
|
|
25
|
+
}
|
|
26
|
+
return value
|
|
27
|
+
},
|
|
28
|
+
}
|
|
20
29
|
const descriptor = (method, parameters = []) => ({
|
|
21
30
|
id: `@foggy-projects/deepseek-harness-plugin#foggyIntegration/${method}`,
|
|
22
31
|
service: 'foggyIntegration',
|
|
@@ -28,6 +37,7 @@ window.__ModuleLoader__.load({
|
|
|
28
37
|
mode: 'strict',
|
|
29
38
|
typeSymbol: '@foggy-projects/deepseek-harness-plugin#FoggyIntegrationResult',
|
|
30
39
|
schema: passthroughSchema,
|
|
40
|
+
create: () => passthroughSchema,
|
|
31
41
|
},
|
|
32
42
|
sourceLocation: { file: 'lib/index.js', line: 1, column: 1 },
|
|
33
43
|
})
|
|
@@ -46,6 +56,16 @@ window.__ModuleLoader__.load({
|
|
|
46
56
|
mode: 'strict',
|
|
47
57
|
typeSymbol: '@foggy-projects/deepseek-harness-plugin#FoggyRuntimeSettingsInput',
|
|
48
58
|
schema: runtimeSettingsInputSchema,
|
|
59
|
+
create: () => runtimeSettingsInputSchema,
|
|
60
|
+
},
|
|
61
|
+
}]),
|
|
62
|
+
descriptor('createViewerPreview', [{
|
|
63
|
+
name: 'input', wire: 'input', source: 'json',
|
|
64
|
+
codec: {
|
|
65
|
+
mode: 'strict',
|
|
66
|
+
typeSymbol: '@foggy-projects/deepseek-harness-plugin#FoggyViewerPreviewInput',
|
|
67
|
+
schema: viewerPreviewInputSchema,
|
|
68
|
+
create: () => viewerPreviewInputSchema,
|
|
49
69
|
},
|
|
50
70
|
}]),
|
|
51
71
|
],
|
|
@@ -147,6 +167,16 @@ window.__ModuleLoader__.load({
|
|
|
147
167
|
queryWarningSummary: 'Runtime 在 WARN 模式下忽略了未知的查询属性,查询仍已执行。请检查告警代码与证据后决定是否修正模型或请求。',
|
|
148
168
|
queryWarningCodes: '告警代码',
|
|
149
169
|
queryWarningEvidence: '证据目录',
|
|
170
|
+
queryCardTitle: 'Foggy DSL 查询',
|
|
171
|
+
queryCardRunning: '正在校验并执行…',
|
|
172
|
+
queryCardDone: '校验与查询成功',
|
|
173
|
+
queryCardFailed: '查询失败;可展开查看错误',
|
|
174
|
+
queryCardRows: '返回行数',
|
|
175
|
+
queryCardDetails: '查看 DSL 与执行结果',
|
|
176
|
+
queryCardOpen: '打开 DataViewer 数据页',
|
|
177
|
+
queryCardOpening: '正在生成预览链接…',
|
|
178
|
+
queryCardPreviewHint: '链接仅在点击时生成,默认有效期 60 分钟;Runtime 重启后失效。这是开发预览,非结果快照。',
|
|
179
|
+
queryCardUnsupported: '当前查询不支持 DataViewer 预览,可继续查看查询结果。',
|
|
150
180
|
stepPlanned: '规划',
|
|
151
181
|
stepDatasourceConfigured: '数据库连接',
|
|
152
182
|
stepDatasourceVerified: '连接验证',
|
|
@@ -272,6 +302,16 @@ window.__ModuleLoader__.load({
|
|
|
272
302
|
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.',
|
|
273
303
|
queryWarningCodes: 'Warning codes',
|
|
274
304
|
queryWarningEvidence: 'Evidence directory',
|
|
305
|
+
queryCardTitle: 'Foggy DSL query',
|
|
306
|
+
queryCardRunning: 'Validating and executing…',
|
|
307
|
+
queryCardDone: 'Validation and query succeeded',
|
|
308
|
+
queryCardFailed: 'Query failed; expand for details',
|
|
309
|
+
queryCardRows: 'Rows',
|
|
310
|
+
queryCardDetails: 'Show DSL and execution result',
|
|
311
|
+
queryCardOpen: 'Open DataViewer',
|
|
312
|
+
queryCardOpening: 'Creating preview link…',
|
|
313
|
+
queryCardPreviewHint: 'The link is created on click, expires after 60 minutes or a Runtime restart, and is a development preview rather than a result snapshot.',
|
|
314
|
+
queryCardUnsupported: 'DataViewer preview is unavailable for this query; the query result remains available.',
|
|
275
315
|
stepPlanned: 'Plan',
|
|
276
316
|
stepDatasourceConfigured: 'Database connection',
|
|
277
317
|
stepDatasourceVerified: 'Connection test',
|
|
@@ -309,7 +349,7 @@ window.__ModuleLoader__.load({
|
|
|
309
349
|
.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)}
|
|
310
350
|
.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;transition:background-color .15s ease,box-shadow .15s ease,transform .15s ease}.foggy-button:hover{background:var(--dsw-alias-interactive-bg-hover)}.foggy-button:active{transform:translateY(1px)}.foggy-button:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}.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}
|
|
311
351
|
.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}
|
|
312
|
-
.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:
|
|
352
|
+
.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:flex-end;gap:12px;margin-top:8px;color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}.foggy-progress-file{justify-content:flex-start;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
313
353
|
.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)}
|
|
314
354
|
.foggy-update-banner{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) 9%,transparent);border-radius:10px;padding:11px 13px;color:var(--dsw-alias-label-primary);font-size:12px;line-height:18px}.foggy-update-banner strong{color:var(--dsw-alias-state-warning-primary)}.foggy-update-list{margin:6px 0 0;padding-left:18px;color:var(--dsw-alias-label-secondary)}
|
|
315
355
|
.foggy-resources{border-top:1px solid var(--dsw-alias-border-l2);padding-top:14px}.foggy-resources h4{margin:0 0 9px;font-size:13px}.foggy-resource-links{display:flex;gap:8px;flex-wrap:wrap}.foggy-resource-link{display:inline-flex;align-items:center;gap:5px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:7px 10px;color:var(--dsw-alias-label-secondary);font-size:12px;text-decoration:none}.foggy-resource-link:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}.foggy-resource-link:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}
|
|
@@ -401,8 +441,9 @@ window.__ModuleLoader__.load({
|
|
|
401
441
|
'runtime-state': 'progressRuntimeState',
|
|
402
442
|
'runtime-complete': 'progressRuntimeComplete',
|
|
403
443
|
}
|
|
404
|
-
const
|
|
405
|
-
const
|
|
444
|
+
const phaseKey = phaseKeys[progress.phase]
|
|
445
|
+
const phase = t(phaseKey || 'progressWorking')
|
|
446
|
+
const headline = phaseKey ? phase : progress.message || phase
|
|
406
447
|
const step = progress.step?.index && progress.step?.total
|
|
407
448
|
? `${progress.step.index}/${progress.step.total}`
|
|
408
449
|
: ''
|
|
@@ -414,6 +455,7 @@ window.__ModuleLoader__.load({
|
|
|
414
455
|
const timing = Number.isFinite(elapsed)
|
|
415
456
|
? `${t('progressElapsed')} ${elapsed}${t('progressSeconds')}${Number.isFinite(timeout) ? ` / ${t('progressTimeout')} ${timeout}${t('progressSeconds')}` : ''}`
|
|
416
457
|
: ''
|
|
458
|
+
const meta = [step, files, timing].filter(Boolean).join(' · ')
|
|
417
459
|
return jsxs('div', {
|
|
418
460
|
className: 'foggy-progress',
|
|
419
461
|
role: 'status',
|
|
@@ -432,10 +474,7 @@ window.__ModuleLoader__.load({
|
|
|
432
474
|
'aria-label': phase,
|
|
433
475
|
children: jsx('div', { className: 'foggy-progress-fill', style: { width: `${percent}%` } }),
|
|
434
476
|
}),
|
|
435
|
-
|
|
436
|
-
jsx('span', { children: phase }),
|
|
437
|
-
jsx('span', { children: [step, files, timing].filter(Boolean).join(' · ') }),
|
|
438
|
-
] }),
|
|
477
|
+
meta ? jsx('div', { className: 'foggy-progress-meta', children: meta }) : null,
|
|
439
478
|
progress.currentFile ? jsx('div', { className: 'foggy-progress-meta foggy-progress-file', title: progress.currentFile, children: progress.currentFile }) : null,
|
|
440
479
|
],
|
|
441
480
|
})
|
|
@@ -552,7 +591,10 @@ window.__ModuleLoader__.load({
|
|
|
552
591
|
for (let attempt = 0; attempt < 900; attempt += 1) {
|
|
553
592
|
const status = unwrap(await api.status(), 'status')
|
|
554
593
|
const operation = status.operation
|
|
555
|
-
if (operation?.id === operationId
|
|
594
|
+
if (operation?.id === operationId) {
|
|
595
|
+
setView((current) => ({ ...current, phase: 'ready', status }))
|
|
596
|
+
if (operation.state !== 'running') return status
|
|
597
|
+
}
|
|
556
598
|
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
557
599
|
}
|
|
558
600
|
throw new Error(t('operationTimeout'))
|
|
@@ -775,6 +817,62 @@ window.__ModuleLoader__.load({
|
|
|
775
817
|
] })
|
|
776
818
|
}
|
|
777
819
|
|
|
820
|
+
function FoggyQueryCard({ phase, block, api, t }) {
|
|
821
|
+
const [preview, setPreview] = useState({ loading: false, url: '', expiresAt: 0, error: '' })
|
|
822
|
+
let args = null
|
|
823
|
+
let result = null
|
|
824
|
+
let outputText = ''
|
|
825
|
+
try {
|
|
826
|
+
const raw = phase === 'result' ? block.call?.argsRaw : phase === 'start' ? block.argsRaw : null
|
|
827
|
+
if (raw) args = JSON.parse(raw)
|
|
828
|
+
if (phase === 'result') {
|
|
829
|
+
outputText = block.content?.find((item) => item.type === 'text')?.text || ''
|
|
830
|
+
if (outputText) result = JSON.parse(outputText)
|
|
831
|
+
}
|
|
832
|
+
} catch {}
|
|
833
|
+
const success = phase === 'result' && !block.isError && Boolean(result?.execution?.success)
|
|
834
|
+
const rows = result?.execution?.data?.items?.length
|
|
835
|
+
const sql = result?.execution?.data?.debug?.extra?.sql
|
|
836
|
+
const payload = args?.payload
|
|
837
|
+
const supported = success && payload && Array.isArray(payload.columns) && payload.columns.length > 0
|
|
838
|
+
&& !('executable_plan' in payload) && !('executablePlan' in payload) && payload.route !== 'DSL_CTE'
|
|
839
|
+
&& Object.keys(payload).every((key) => ['columns', 'slice', 'groupBy', 'orderBy', 'calculatedFields', 'extData', 'start', 'limit', 'returnTotal'].includes(key))
|
|
840
|
+
&& ['slice', 'groupBy', 'orderBy', 'calculatedFields'].every((key) => !payload[key] || Array.isArray(payload[key]) && payload[key].every((item) => item && typeof item === 'object' && !Array.isArray(item)))
|
|
841
|
+
async function openViewer() {
|
|
842
|
+
const pendingTab = window.open('about:blank', '_blank')
|
|
843
|
+
setPreview({ loading: true, url: '', expiresAt: 0, error: '' })
|
|
844
|
+
try {
|
|
845
|
+
const value = unwrap(await api.createViewerPreview({ model: args.model, namespace: args.namespace, payload }), 'createViewerPreview')
|
|
846
|
+
setPreview({ loading: false, url: value.url, expiresAt: Date.now() + value.expiresInMinutes * 60_000, error: '' })
|
|
847
|
+
if (pendingTab) pendingTab.location.replace(value.url)
|
|
848
|
+
} catch (error) {
|
|
849
|
+
if (pendingTab) pendingTab.close()
|
|
850
|
+
setPreview({ loading: false, url: '', expiresAt: 0, error: String(error?.message || error) })
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
const box = { border: '1px solid var(--dsw-alias-border-l2)', borderRadius: 10, padding: 12, margin: '8px 0', fontSize: 13 }
|
|
854
|
+
return jsxs('section', { style: box, children: [
|
|
855
|
+
jsx('strong', { children: `${t('queryCardTitle')}${args?.model ? ` · ${args.model}` : ''}` }),
|
|
856
|
+
jsx('p', { style: { margin: '6px 0', color: 'var(--dsw-alias-label-secondary)' }, children: success ? `${t('queryCardDone')}${Number.isInteger(rows) ? ` · ${t('queryCardRows')} ${rows}` : ''}` : phase === 'result' ? t('queryCardFailed') : t('queryCardRunning') }),
|
|
857
|
+
args?.namespace ? jsx('code', { children: `namespace: ${args.namespace}` }) : null,
|
|
858
|
+
success ? jsxs('details', { style: { marginTop: 8 }, children: [
|
|
859
|
+
jsx('summary', { style: { cursor: 'pointer' }, children: t('queryCardDetails') }),
|
|
860
|
+
sql ? jsx('pre', { style: { maxHeight: 180, overflow: 'auto', whiteSpace: 'pre-wrap', overflowWrap: 'anywhere' }, children: sql }) : null,
|
|
861
|
+
jsx('pre', { style: { maxHeight: 260, overflow: 'auto', whiteSpace: 'pre-wrap', overflowWrap: 'anywhere' }, children: JSON.stringify({ dsl: payload, validation: result.validation, execution: result.execution }, null, 2).slice(0, 30000) }),
|
|
862
|
+
] }) : null,
|
|
863
|
+
phase === 'result' && !success ? jsxs('details', { style: { marginTop: 8 }, children: [
|
|
864
|
+
jsx('summary', { style: { cursor: 'pointer' }, children: t('queryCardFailed') }),
|
|
865
|
+
jsx('pre', { style: { maxHeight: 260, overflow: 'auto', whiteSpace: 'pre-wrap', overflowWrap: 'anywhere' }, children: (outputText || JSON.stringify(block)).slice(0, 6000) }),
|
|
866
|
+
] }) : null,
|
|
867
|
+
supported ? jsxs('div', { style: { marginTop: 10 }, children: [
|
|
868
|
+
jsx('button', { type: 'button', disabled: preview.loading, onClick: openViewer, children: preview.loading ? t('queryCardOpening') : t('queryCardOpen') }),
|
|
869
|
+
jsx('p', { style: { margin: '5px 0', color: 'var(--dsw-alias-label-tertiary)', fontSize: 11 }, children: t('queryCardPreviewHint') }),
|
|
870
|
+
preview.url && Date.now() < preview.expiresAt ? jsx('a', { href: preview.url, target: '_blank', rel: 'noopener noreferrer', children: preview.url }) : null,
|
|
871
|
+
preview.error ? jsx('p', { role: 'alert', style: { color: 'var(--dsw-alias-state-error-primary)' }, children: preview.error }) : null,
|
|
872
|
+
] }) : success ? jsx('p', { style: { margin: '8px 0 0', color: 'var(--dsw-alias-label-tertiary)' }, children: t('queryCardUnsupported') }) : null,
|
|
873
|
+
] })
|
|
874
|
+
}
|
|
875
|
+
|
|
778
876
|
const inject = ['slots', 'locale', 'remote']
|
|
779
877
|
|
|
780
878
|
async function apply(ctx) {
|
|
@@ -784,6 +882,11 @@ window.__ModuleLoader__.load({
|
|
|
784
882
|
const t = ctx.locale.bind(NS)
|
|
785
883
|
ctx.inject(['remote.foggyIntegration'], (remoteCtx) => {
|
|
786
884
|
const api = remoteCtx.remote.foggyIntegration
|
|
885
|
+
remoteCtx.slots.inject('tool.call.toolview', () => remoteCtx.slots.register({
|
|
886
|
+
name: 'tool.call.toolview',
|
|
887
|
+
key: 'foggy_query',
|
|
888
|
+
inject: () => ({ api, t }),
|
|
889
|
+
}, FoggyQueryCard))
|
|
787
890
|
remoteCtx.slots.inject('settings.plugins.tab', () => remoteCtx.slots.register({
|
|
788
891
|
name: 'settings.plugins.tab',
|
|
789
892
|
id: 'foggy',
|
package/lib/index.js
CHANGED
|
@@ -10,6 +10,8 @@ import { writeJsonAtomic } from './atomic-json.js'
|
|
|
10
10
|
import { ensurePythonRuntime, probePythonRuntime } from './python-runtime.js'
|
|
11
11
|
import { compatible, compatibleNode } from './version.js'
|
|
12
12
|
import { enrichRuntimeStartFailure } from './diagnostics.js'
|
|
13
|
+
import { createFoggyQueryTool, managedConnection } from './query-tool.js'
|
|
14
|
+
import { createViewerPreview } from './viewer-preview.js'
|
|
13
15
|
import {
|
|
14
16
|
DEFAULT_RUNTIME_PORT,
|
|
15
17
|
readRuntimeSettings,
|
|
@@ -210,7 +212,7 @@ function operationView(operation) {
|
|
|
210
212
|
}
|
|
211
213
|
|
|
212
214
|
export class FoggyIntegrationGateway extends TypertRemoteService {
|
|
213
|
-
static inject = ['skills']
|
|
215
|
+
static inject = ['skills', 'tools']
|
|
214
216
|
|
|
215
217
|
operation = null
|
|
216
218
|
skillProviderControl = null
|
|
@@ -222,9 +224,15 @@ export class FoggyIntegrationGateway extends TypertRemoteService {
|
|
|
222
224
|
this.skillProviderControl = control
|
|
223
225
|
return createFoggySkillProvider({ installRoot: roots.installRoot, versionsFile })
|
|
224
226
|
})
|
|
227
|
+
ctx.tools.register(createFoggyQueryTool(roots))
|
|
225
228
|
for (const initialize of markerInitializers) initialize.call(this)
|
|
226
229
|
}
|
|
227
230
|
|
|
231
|
+
async createViewerPreview(input) {
|
|
232
|
+
const { runtimeUrl } = await managedConnection(defaultRoots())
|
|
233
|
+
return createViewerPreview(input, runtimeUrl)
|
|
234
|
+
}
|
|
235
|
+
|
|
228
236
|
async status() {
|
|
229
237
|
const roots = defaultRoots()
|
|
230
238
|
const statePath = join(roots.installRoot, 'install-state.json')
|
|
@@ -672,6 +680,7 @@ for (const method of [
|
|
|
672
680
|
'status', 'plan', 'initialize', 'initializeAndStart', 'repair', 'updateComponents', 'repairPython', 'repairCli', 'repairLauncher', 'repairAnalysisSkill', 'repairSemanticQuerySkill',
|
|
673
681
|
'migrateProfiles', 'diagnostics', 'runtimeStart', 'runtimeStop',
|
|
674
682
|
'saveRuntimeSettings',
|
|
683
|
+
'createViewerPreview',
|
|
675
684
|
]) {
|
|
676
685
|
Remote(method)(FoggyIntegrationGateway.prototype[method], {
|
|
677
686
|
kind: 'method',
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process'
|
|
2
|
+
import { mkdtemp, readFile, rmdir, unlink, writeFile } from 'node:fs/promises'
|
|
3
|
+
import { tmpdir } from 'node:os'
|
|
4
|
+
import { join } from 'node:path'
|
|
5
|
+
import { promisify } from 'node:util'
|
|
6
|
+
import { defineTool } from '@deepseek-ai/dsh-tools'
|
|
7
|
+
|
|
8
|
+
const execFileAsync = promisify(execFile)
|
|
9
|
+
const identifier = /^[A-Za-z0-9_.-]{1,128}$/
|
|
10
|
+
|
|
11
|
+
export function assertStandardDsl(payload) {
|
|
12
|
+
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) throw new Error('Query DSL must be a JSON object')
|
|
13
|
+
if (payload.route === 'DSL_CTE' || 'executable_plan' in payload || 'executablePlan' in payload) {
|
|
14
|
+
throw new Error('CTE preview is not supported in this release; use a standard QM DSL query')
|
|
15
|
+
}
|
|
16
|
+
if (!Array.isArray(payload.columns) || payload.columns.length === 0 || !payload.columns.every((item) => typeof item === 'string')) {
|
|
17
|
+
throw new Error('Standard QM DSL requires non-empty columns')
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function previewPayload(payload) {
|
|
22
|
+
assertStandardDsl(payload)
|
|
23
|
+
const supported = new Set(['columns', 'slice', 'groupBy', 'orderBy', 'calculatedFields', 'extData'])
|
|
24
|
+
const unsupported = Object.keys(payload).filter((key) => !supported.has(key) && !['start', 'limit', 'returnTotal'].includes(key))
|
|
25
|
+
if (unsupported.length) throw new Error(`DataViewer preview does not support: ${unsupported.join(', ')}`)
|
|
26
|
+
for (const key of ['slice', 'groupBy', 'orderBy', 'calculatedFields']) {
|
|
27
|
+
if (payload[key] !== undefined && (!Array.isArray(payload[key]) || payload[key].some((item) => !item || typeof item !== 'object' || Array.isArray(item)))) {
|
|
28
|
+
throw new Error(`DataViewer preview requires object-form ${key} entries`)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return Object.fromEntries([...supported].filter((key) => payload[key] !== undefined).map((key) => [key, payload[key]]))
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function safeRuntimeUrl(value) {
|
|
35
|
+
const url = new URL(value)
|
|
36
|
+
if (url.protocol !== 'http:' || !['127.0.0.1', 'localhost', '[::1]'].includes(url.hostname)
|
|
37
|
+
|| url.username || url.password || url.pathname !== '/' || url.search || url.hash) {
|
|
38
|
+
throw new Error('Managed Runtime URL must be a plain loopback HTTP origin')
|
|
39
|
+
}
|
|
40
|
+
return url.origin
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function managedConnection(roots) {
|
|
44
|
+
const install = JSON.parse(await readFile(join(roots.installRoot, 'install-state.json'), 'utf8'))
|
|
45
|
+
const runtime = JSON.parse(await readFile(join(roots.dataRoot, 'runtime-state.json'), 'utf8'))
|
|
46
|
+
if (!install.cli?.command) throw new Error('Foggy CLI is not installed')
|
|
47
|
+
return { cli: install.cli.command, runtimeUrl: safeRuntimeUrl(runtime.runtimeUrl) }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function cliJson(cli, runtimeUrl, namespace, command, signal) {
|
|
51
|
+
const { stdout } = await execFileAsync(cli, ['--base-url', runtimeUrl, '--namespace', namespace, '--output', 'json', ...command], {
|
|
52
|
+
signal,
|
|
53
|
+
windowsHide: true,
|
|
54
|
+
timeout: 120_000,
|
|
55
|
+
maxBuffer: 8 * 1024 * 1024,
|
|
56
|
+
})
|
|
57
|
+
const value = JSON.parse(stdout.trim())
|
|
58
|
+
if (value?.success !== true) throw new Error(value?.error?.message || value?.error?.code || 'Foggy query failed')
|
|
59
|
+
return value
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function createFoggyQueryTool(roots) {
|
|
63
|
+
return defineTool({
|
|
64
|
+
name: 'foggy_query',
|
|
65
|
+
description: 'Validate and execute a bounded, read-only standard Foggy QueryModel DSL query against the managed local Runtime. On success Harness shows a query card; its DataViewer preview link is created only if the user clicks Open DataViewer. CTE is not supported here.',
|
|
66
|
+
parameters: {
|
|
67
|
+
model: { type: 'string', required: true, description: 'Published Foggy QueryModel name' },
|
|
68
|
+
namespace: { type: 'string', required: true, description: 'Foggy namespace' },
|
|
69
|
+
payload: { type: 'object', additionalProperties: true, required: true, description: 'Standard QueryModel DSL, including explicit columns and bounded limit' },
|
|
70
|
+
},
|
|
71
|
+
output: {
|
|
72
|
+
schema: { type: 'object', additionalProperties: false, properties: {
|
|
73
|
+
model: { type: 'string', required: true },
|
|
74
|
+
namespace: { type: 'string', required: true },
|
|
75
|
+
validation: { type: 'json', required: true },
|
|
76
|
+
execution: { type: 'json', required: true },
|
|
77
|
+
} },
|
|
78
|
+
render: (_args, value) => [{ type: 'text', text: JSON.stringify(value) }],
|
|
79
|
+
},
|
|
80
|
+
timeoutMs: 150_000,
|
|
81
|
+
async execute({ model, namespace, payload }, exec) {
|
|
82
|
+
if (!identifier.test(model) || !identifier.test(namespace)) throw new Error('Invalid model or namespace')
|
|
83
|
+
assertStandardDsl(payload)
|
|
84
|
+
if (!Number.isInteger(payload.limit) || payload.limit < 1 || payload.limit > 100) {
|
|
85
|
+
throw new Error('Query DSL limit must be an integer from 1 to 100')
|
|
86
|
+
}
|
|
87
|
+
const { cli, runtimeUrl } = await managedConnection(roots)
|
|
88
|
+
const directory = await mkdtemp(join(tmpdir(), 'foggy-dsh-query-'))
|
|
89
|
+
const payloadPath = join(directory, 'query.json')
|
|
90
|
+
try {
|
|
91
|
+
await writeFile(payloadPath, JSON.stringify(payload), { encoding: 'utf8', flag: 'wx' })
|
|
92
|
+
const validation = await cliJson(cli, runtimeUrl, namespace, ['query', 'validate', model, '--payload', payloadPath], exec.signal)
|
|
93
|
+
const execution = await cliJson(cli, runtimeUrl, namespace, ['query', 'execute', model, '--payload', payloadPath], exec.signal)
|
|
94
|
+
return { model, namespace, validation, execution }
|
|
95
|
+
} finally {
|
|
96
|
+
await unlink(payloadPath).catch(() => {})
|
|
97
|
+
await rmdir(directory).catch(() => {})
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
presentCall({ model }) {
|
|
101
|
+
return { card: 'generic', title: `Foggy DSL · ${model}`, kind: 'read' }
|
|
102
|
+
},
|
|
103
|
+
})
|
|
104
|
+
}
|
package/lib/remote-descriptor.js
CHANGED
|
@@ -4,6 +4,11 @@ const resultSchema = z.unknown()
|
|
|
4
4
|
const runtimeSettingsInputSchema = z.object({
|
|
5
5
|
port: z.number().int().min(1024).max(65535),
|
|
6
6
|
}).strict()
|
|
7
|
+
const viewerPreviewInputSchema = z.object({
|
|
8
|
+
model: z.string(),
|
|
9
|
+
namespace: z.string(),
|
|
10
|
+
payload: z.record(z.string(), z.unknown()),
|
|
11
|
+
}).strict()
|
|
7
12
|
|
|
8
13
|
function strictCodec(typeSymbol, schema) {
|
|
9
14
|
return {
|
|
@@ -45,4 +50,10 @@ export const descriptors = [
|
|
|
45
50
|
source: 'json',
|
|
46
51
|
codec: strictCodec('@foggy-projects/deepseek-harness-plugin#FoggyRuntimeSettingsInput', runtimeSettingsInputSchema),
|
|
47
52
|
}]),
|
|
53
|
+
descriptor('createViewerPreview', [{
|
|
54
|
+
name: 'input',
|
|
55
|
+
wire: 'input',
|
|
56
|
+
source: 'json',
|
|
57
|
+
codec: strictCodec('@foggy-projects/deepseek-harness-plugin#FoggyViewerPreviewInput', viewerPreviewInputSchema),
|
|
58
|
+
}]),
|
|
48
59
|
]
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { previewPayload, safeRuntimeUrl } from './query-tool.js'
|
|
2
|
+
|
|
3
|
+
const identifier = /^[A-Za-z0-9_.-]{1,128}$/
|
|
4
|
+
|
|
5
|
+
export async function createViewerPreview({ model, namespace, payload }, runtimeUrl, fetcher = fetch) {
|
|
6
|
+
if (!identifier.test(model) || !identifier.test(namespace)) throw new Error('Invalid model or namespace')
|
|
7
|
+
const origin = safeRuntimeUrl(runtimeUrl)
|
|
8
|
+
const body = { model, namespace, title: `${model} · Harness preview`, payload: previewPayload(payload) }
|
|
9
|
+
const response = await fetcher(`${origin}/data-viewer/api/query/create`, {
|
|
10
|
+
method: 'POST',
|
|
11
|
+
headers: { 'content-type': 'application/json' },
|
|
12
|
+
body: JSON.stringify(body),
|
|
13
|
+
signal: AbortSignal.timeout(12_000),
|
|
14
|
+
})
|
|
15
|
+
if (!response.ok) throw new Error(`DataViewer unavailable (HTTP ${response.status}); update and restart the managed Runtime`)
|
|
16
|
+
const envelope = await response.json()
|
|
17
|
+
const result = envelope?.data
|
|
18
|
+
if (envelope?.code !== 200 || result?.success !== true || typeof result.viewerUrl !== 'string') {
|
|
19
|
+
throw new Error(envelope?.msg || result?.error || 'DataViewer did not return a preview link')
|
|
20
|
+
}
|
|
21
|
+
const url = new URL(result.viewerUrl, origin)
|
|
22
|
+
if (url.origin !== origin || !url.pathname.startsWith(`/data-viewer/view/${encodeURIComponent(model)}/`)
|
|
23
|
+
|| url.username || url.password || url.search || url.hash) {
|
|
24
|
+
throw new Error('DataViewer returned an unexpected preview URL')
|
|
25
|
+
}
|
|
26
|
+
return { success: true, url: url.href, queryId: result.queryId, expiresInMinutes: 60 }
|
|
27
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@foggy-projects/deepseek-harness-plugin",
|
|
3
|
-
"version": "0.4.2-rc.
|
|
3
|
+
"version": "0.4.2-rc.3",
|
|
4
4
|
"description": "Foggy Java data analysis engine integration for DeepSeek Harness",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"deepseek-harness",
|
|
@@ -40,6 +40,8 @@
|
|
|
40
40
|
"docs/RELEASE-CANDIDATE-0.4.0-RC.2.md",
|
|
41
41
|
"docs/RELEASE-CANDIDATE-0.4.0-RC.3.md",
|
|
42
42
|
"docs/RELEASE-CANDIDATE-0.4.1-DSH-0.1.5-RC.1.md",
|
|
43
|
+
"docs/RELEASE-CANDIDATE-0.4.2-DSH-0.1.7-RC.2.md",
|
|
44
|
+
"docs/RELEASE-CANDIDATE-0.4.2-DSH-0.1.7-RC.3.md",
|
|
43
45
|
"docs/RELEASE-CANDIDATE-0.4.2-DSH-0.1.7-RC.1.md",
|
|
44
46
|
"docs/COMMUNITY-MARKETPLACES.md",
|
|
45
47
|
"docs/WINDOWS-BETA-ACCEPTANCE.md",
|
|
@@ -60,11 +62,13 @@
|
|
|
60
62
|
"@deepseek-ai/dsh-api-remotes",
|
|
61
63
|
"@deepseek-ai/dsh-client-locale",
|
|
62
64
|
"@deepseek-ai/dsh-client-ui-settings",
|
|
63
|
-
"@deepseek-ai/dsh-client-ui-settings-plugins"
|
|
65
|
+
"@deepseek-ai/dsh-client-ui-settings-plugins",
|
|
66
|
+
"@deepseek-ai/dsh-client-ui-tool"
|
|
64
67
|
]
|
|
65
68
|
}
|
|
66
69
|
},
|
|
67
70
|
"dependencies": {
|
|
71
|
+
"@deepseek-ai/dsh-tools": "0.1.7-rc.2",
|
|
68
72
|
"zod": "^4.4.3"
|
|
69
73
|
},
|
|
70
74
|
"peerDependencies": {
|
|
@@ -73,11 +77,12 @@
|
|
|
73
77
|
"@deepseek-ai/dsh-client-locale": "^0.1.7-rc.2",
|
|
74
78
|
"@deepseek-ai/dsh-client-ui-settings": "^0.1.7-rc.2",
|
|
75
79
|
"@deepseek-ai/dsh-client-ui-settings-plugins": "^0.1.7-rc.2",
|
|
80
|
+
"@deepseek-ai/dsh-client-ui-tool": "^0.1.7-rc.2",
|
|
76
81
|
"@deepseek-ai/dsh-skill": "^0.1.7-rc.2",
|
|
77
82
|
"@deepseek-ai/dsh-typert-protocol": "^0.1.7-rc.2"
|
|
78
83
|
},
|
|
79
84
|
"scripts": {
|
|
80
|
-
"check": "node --check lib/index.js && node --check lib/atomic-json.js && node --check lib/diagnostics.js && node --check lib/python-runtime.js && node --check lib/runtime-settings.js && node --check lib/skill-provider.js && node --check lib/client.js && node --check lib/typert.js && node --check lib/remote.js",
|
|
81
|
-
"test": "node --test test/package.test.js && node test/run-onboarding-tests.js"
|
|
85
|
+
"check": "node --check lib/index.js && node --check lib/query-tool.js && node --check lib/viewer-preview.js && node --check lib/atomic-json.js && node --check lib/diagnostics.js && node --check lib/python-runtime.js && node --check lib/runtime-settings.js && node --check lib/skill-provider.js && node --check lib/client.js && node --check lib/typert.js && node --check lib/remote.js",
|
|
86
|
+
"test": "node --test test/package.test.js test/viewer-preview.test.js && node test/run-onboarding-tests.js"
|
|
82
87
|
}
|
|
83
88
|
}
|
|
@@ -40,8 +40,9 @@ are authoritative; do not copy the Skill into the current workspace.
|
|
|
40
40
|
|
|
41
41
|
## Development workflow
|
|
42
42
|
|
|
43
|
-
1. Run `doctor`.
|
|
44
|
-
|
|
43
|
+
1. Run `doctor`. Runtime health is established by the managed CLI's read-only `wait-ready` check;
|
|
44
|
+
process visibility alone does not prove HTTP readiness. Start Runtime only if it is not already
|
|
45
|
+
healthy; require `wait-ready` and `capabilities` after a new start.
|
|
45
46
|
2. Accept datasource connection details from the user's message, a user-supplied local JSON file, an
|
|
46
47
|
environment variable, or Runtime Console. Direct `password` is supported for local development.
|
|
47
48
|
The wrapper submits it to the public Runtime API without copying it into onboarding state or
|
|
@@ -59,6 +60,12 @@ are authoritative; do not copy the Skill into the current workspace.
|
|
|
59
60
|
6. Iterate through model validation, Bundle registration/update, model refresh/describe, query
|
|
60
61
|
validation, and bounded query execution. Development publication means making the local model
|
|
61
62
|
directory effective in this local Runtime; it is not a production release.
|
|
63
|
+
For the final user-facing standard DSL query in Harness, use the native `foggy_query` tool when
|
|
64
|
+
available (explicit namespace, published QM, `columns`, and `limit` of 1–100). It validates and
|
|
65
|
+
executes, then displays a query card. The card creates a DataViewer preview link only if the user
|
|
66
|
+
clicks its button; never pre-create links for every query. This first version does not support
|
|
67
|
+
`DSL_CTE` or `executable_plan` in the native tool. Continue using the documented CLI route for
|
|
68
|
+
CTE queries and do not claim they have a DataViewer preview.
|
|
62
69
|
A successful response may still contain Runtime `warnings`. Preserve and report their stable facts
|
|
63
70
|
(`code`, `path`, `message`, `suggestedNextAction`, `safeToAutoRepair`, `normalizedFragment`,
|
|
64
71
|
`docsRef`, and `details.allowedProperties`)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": "foggy-deepseek-onboarding-versions/v1",
|
|
3
|
-
"packageVersion": "0.4.2-rc.
|
|
4
|
-
"validatedAt": "2026-09-
|
|
3
|
+
"packageVersion": "0.4.2-rc.3",
|
|
4
|
+
"validatedAt": "2026-09-28",
|
|
5
5
|
"components": {
|
|
6
6
|
"deepseekHarness": {
|
|
7
7
|
"version": "0.1.7-rc.2",
|
|
@@ -66,39 +66,39 @@
|
|
|
66
66
|
}
|
|
67
67
|
},
|
|
68
68
|
"launcher": {
|
|
69
|
-
"version": "0.1.
|
|
69
|
+
"version": "0.1.22",
|
|
70
70
|
"minimumJavaVersion": "17.0.0",
|
|
71
71
|
"runtimeApiContract": "foggy-runtime-api/v1",
|
|
72
72
|
"assets": [
|
|
73
73
|
{
|
|
74
|
-
"file": "foggy-runtime-launcher-0.1.
|
|
75
|
-
"url": "https://github.com/foggy-projects/foggy-data-mcp-bridge/releases/download/foggy-runtime-launcher-v0.1.
|
|
76
|
-
"sha256": "
|
|
74
|
+
"file": "foggy-runtime-launcher-0.1.22.jar",
|
|
75
|
+
"url": "https://github.com/foggy-projects/foggy-data-mcp-bridge/releases/download/foggy-runtime-launcher-v0.1.22/foggy-runtime-launcher-0.1.22.jar",
|
|
76
|
+
"sha256": "c26d21ff50323d6adc3124e084462f8381407c6f79802cefac90f4ddafa00dca"
|
|
77
77
|
},
|
|
78
78
|
{
|
|
79
79
|
"file": "start-foggy-runtime.ps1",
|
|
80
|
-
"url": "https://github.com/foggy-projects/foggy-data-mcp-bridge/releases/download/foggy-runtime-launcher-v0.1.
|
|
81
|
-
"sha256": "
|
|
80
|
+
"url": "https://github.com/foggy-projects/foggy-data-mcp-bridge/releases/download/foggy-runtime-launcher-v0.1.22/start-foggy-runtime.ps1",
|
|
81
|
+
"sha256": "dcfaa3d4e512e72f16e6d44dda00ae27f0cfc164763cac73c0ee63fd4b7236bc"
|
|
82
82
|
},
|
|
83
83
|
{
|
|
84
84
|
"file": "start-foggy-runtime.sh",
|
|
85
|
-
"url": "https://github.com/foggy-projects/foggy-data-mcp-bridge/releases/download/foggy-runtime-launcher-v0.1.
|
|
86
|
-
"sha256": "
|
|
85
|
+
"url": "https://github.com/foggy-projects/foggy-data-mcp-bridge/releases/download/foggy-runtime-launcher-v0.1.22/start-foggy-runtime.sh",
|
|
86
|
+
"sha256": "fa304f66679f0323157ea7948fd95fb1082264f115b9b4d688b1995c24b3c1d7"
|
|
87
87
|
},
|
|
88
88
|
{
|
|
89
89
|
"file": "README-foggy-runtime-launcher.md",
|
|
90
|
-
"url": "https://github.com/foggy-projects/foggy-data-mcp-bridge/releases/download/foggy-runtime-launcher-v0.1.
|
|
91
|
-
"sha256": "
|
|
90
|
+
"url": "https://github.com/foggy-projects/foggy-data-mcp-bridge/releases/download/foggy-runtime-launcher-v0.1.22/README-foggy-runtime-launcher.md",
|
|
91
|
+
"sha256": "43711229b425f13fa242d58916ef17cbd3dcebe4fc18b951aa9bf4befc056c26"
|
|
92
92
|
},
|
|
93
93
|
{
|
|
94
94
|
"file": "runtime-launcher-manifest.json",
|
|
95
|
-
"url": "https://github.com/foggy-projects/foggy-data-mcp-bridge/releases/download/foggy-runtime-launcher-v0.1.
|
|
96
|
-
"sha256": "
|
|
95
|
+
"url": "https://github.com/foggy-projects/foggy-data-mcp-bridge/releases/download/foggy-runtime-launcher-v0.1.22/runtime-launcher-manifest.json",
|
|
96
|
+
"sha256": "2b9efb82e8f954f2407f36ae419984068d384e1e0b86796ccfad0981dc7b2a30"
|
|
97
97
|
},
|
|
98
98
|
{
|
|
99
99
|
"file": "SHA256SUMS",
|
|
100
|
-
"url": "https://github.com/foggy-projects/foggy-data-mcp-bridge/releases/download/foggy-runtime-launcher-v0.1.
|
|
101
|
-
"sha256": "
|
|
100
|
+
"url": "https://github.com/foggy-projects/foggy-data-mcp-bridge/releases/download/foggy-runtime-launcher-v0.1.22/SHA256SUMS",
|
|
101
|
+
"sha256": "51dc530e275bc1c9e9c73e9fce13d236fd0c5e3bfcebd9b03b837c5bc05e59f8"
|
|
102
102
|
}
|
|
103
103
|
]
|
|
104
104
|
},
|
|
@@ -1790,8 +1790,9 @@ def onboarding_context(args: argparse.Namespace, require_runtime: bool = False)
|
|
|
1790
1790
|
if require_runtime:
|
|
1791
1791
|
if not runtime_state:
|
|
1792
1792
|
raise OnboardingError("Runtime is not started; run runtime-start first")
|
|
1793
|
-
|
|
1794
|
-
|
|
1793
|
+
health = runtime_health(install_state, runtime_state)
|
|
1794
|
+
if health["status"] != "running":
|
|
1795
|
+
raise OnboardingError("Runtime readiness could not be verified; run doctor and inspect Runtime health before continuing")
|
|
1795
1796
|
return install_root, install_state, data_root, runtime_state
|
|
1796
1797
|
|
|
1797
1798
|
|
|
@@ -1814,6 +1815,52 @@ def cli_json(install_state: dict, runtime_state: dict, namespace: str, command:
|
|
|
1814
1815
|
return payload
|
|
1815
1816
|
|
|
1816
1817
|
|
|
1818
|
+
def runtime_health(install_state: dict, runtime_state: dict) -> dict:
|
|
1819
|
+
"""Use Runtime API readiness as the health signal; process inspection is diagnostic only."""
|
|
1820
|
+
pid = runtime_state.get("pid")
|
|
1821
|
+
info = process_info(int(pid or 0))
|
|
1822
|
+
base = {
|
|
1823
|
+
"pid": pid,
|
|
1824
|
+
"runtimeUrl": runtime_state.get("runtimeUrl"),
|
|
1825
|
+
"identity": runtime_state.get("identity"),
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
try:
|
|
1829
|
+
probe = cli_json(
|
|
1830
|
+
install_state,
|
|
1831
|
+
runtime_state,
|
|
1832
|
+
runtime_state.get("namespace") or "default",
|
|
1833
|
+
["wait-ready", "--timeout-seconds", "2", "--interval-seconds", "1"],
|
|
1834
|
+
"Runtime readiness probe",
|
|
1835
|
+
timeout=5,
|
|
1836
|
+
)
|
|
1837
|
+
except OnboardingError:
|
|
1838
|
+
return {
|
|
1839
|
+
**base,
|
|
1840
|
+
"status": "unverified",
|
|
1841
|
+
"verifiedBy": "none",
|
|
1842
|
+
"processInspection": "confirmed" if info["running"] else "not-confirmed",
|
|
1843
|
+
"readiness": "failed",
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1846
|
+
data = probe.get("data")
|
|
1847
|
+
if isinstance(data, dict) and data.get("ready") is True:
|
|
1848
|
+
return {
|
|
1849
|
+
**base,
|
|
1850
|
+
"status": "running",
|
|
1851
|
+
"verifiedBy": "runtime-api",
|
|
1852
|
+
"processInspection": "confirmed" if info["running"] else "not-confirmed",
|
|
1853
|
+
"readiness": "ready",
|
|
1854
|
+
}
|
|
1855
|
+
return {
|
|
1856
|
+
**base,
|
|
1857
|
+
"status": "unverified",
|
|
1858
|
+
"verifiedBy": "none",
|
|
1859
|
+
"processInspection": "confirmed" if info["running"] else "not-confirmed",
|
|
1860
|
+
"readiness": "not-ready",
|
|
1861
|
+
}
|
|
1862
|
+
|
|
1863
|
+
|
|
1817
1864
|
TRANSIENT_DATASOURCE_TEST_MARKERS = (
|
|
1818
1865
|
"connection is not available",
|
|
1819
1866
|
"connection pool",
|
|
@@ -2741,7 +2788,7 @@ def next_onboarding_action(state: dict) -> dict:
|
|
|
2741
2788
|
|
|
2742
2789
|
def onboarding_status_command(args: argparse.Namespace) -> dict:
|
|
2743
2790
|
state, _install_state, _data_root, runtime_state = require_profile(args, require_runtime=False)
|
|
2744
|
-
runtime_running = bool(runtime_state and
|
|
2791
|
+
runtime_running = bool(runtime_state and runtime_health(_install_state, runtime_state)["status"] == "running")
|
|
2745
2792
|
connection = state["connection"]
|
|
2746
2793
|
password_env = connection.get("passwordEnv")
|
|
2747
2794
|
return {
|
|
@@ -3356,8 +3403,7 @@ def doctor_command(args: argparse.Namespace) -> dict:
|
|
|
3356
3403
|
runtime_state_path = data_root / "runtime-state.json"
|
|
3357
3404
|
if runtime_state_path.is_file():
|
|
3358
3405
|
runtime_state = json.loads(runtime_state_path.read_text(encoding="utf-8"))
|
|
3359
|
-
|
|
3360
|
-
runtime = {"status": "running" if info["running"] else "stale", "pid": runtime_state.get("pid"), "runtimeUrl": runtime_state.get("runtimeUrl"), "identity": runtime_state.get("identity")}
|
|
3406
|
+
runtime = runtime_health(state, runtime_state)
|
|
3361
3407
|
required = {
|
|
3362
3408
|
"python": python_ok,
|
|
3363
3409
|
"java": java_ok,
|