@aryacode/dsh-engineering 0.0.0-stage → 0.4.0
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/CHANGELOG.md +128 -0
- package/LICENSE +21 -0
- package/README.md +230 -2
- package/cordis.patch.yml +123 -0
- package/lib/client.js +3448 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +9 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +94 -3
- package/vendor/ftview/README.md +55 -0
- package/vendor/ftview/SKILL.md +74 -0
- package/vendor/ftview/lib/checks.d.ts +27 -0
- package/vendor/ftview/lib/checks.js +39 -0
- package/vendor/ftview/lib/core.d.ts +208 -0
- package/vendor/ftview/lib/core.js +160 -0
- package/vendor/ftview/lib/edit/drafts.d.ts +49 -0
- package/vendor/ftview/lib/edit/drafts.js +84 -0
- package/vendor/ftview/lib/edit/operations.d.ts +62 -0
- package/vendor/ftview/lib/edit/operations.js +126 -0
- package/vendor/ftview/lib/edit/prepare.d.ts +38 -0
- package/vendor/ftview/lib/edit/prepare.js +133 -0
- package/vendor/ftview/lib/edit/tools.d.ts +25 -0
- package/vendor/ftview/lib/edit/tools.js +198 -0
- package/vendor/ftview/lib/edit.d.ts +30 -0
- package/vendor/ftview/lib/edit.js +38 -0
- package/vendor/ftview/lib/index.d.ts +31 -0
- package/vendor/ftview/lib/index.js +36 -0
- package/vendor/ftview/lib/model.d.ts +80 -0
- package/vendor/ftview/lib/model.js +267 -0
- package/vendor/ftview/lib/session.d.ts +48 -0
- package/vendor/ftview/lib/session.js +98 -0
- package/vendor/ftview/lib/skill.d.ts +29 -0
- package/vendor/ftview/lib/skill.js +43 -0
- package/vendor/ftview/lib/studio/discover.d.ts +36 -0
- package/vendor/ftview/lib/studio/discover.js +87 -0
- package/vendor/ftview/lib/studio/preflight.d.ts +51 -0
- package/vendor/ftview/lib/studio/preflight.js +64 -0
- package/vendor/ftview/lib/studio/tools.d.ts +15 -0
- package/vendor/ftview/lib/studio/tools.js +83 -0
- package/vendor/ftview/lib/studio.d.ts +30 -0
- package/vendor/ftview/lib/studio.js +37 -0
- package/vendor/ftview/lib/tools.d.ts +14 -0
- package/vendor/ftview/lib/tools.js +202 -0
- package/vendor/l5x/README.md +62 -0
- package/vendor/l5x/SKILL.md +81 -0
- package/vendor/l5x/lib/approvals.d.ts +50 -0
- package/vendor/l5x/lib/approvals.js +93 -0
- package/vendor/l5x/lib/client.js +412 -0
- package/vendor/l5x/lib/client.js.map +1 -0
- package/vendor/l5x/lib/core/errors.d.ts +38 -0
- package/vendor/l5x/lib/core/errors.js +48 -0
- package/vendor/l5x/lib/core/params.d.ts +49 -0
- package/vendor/l5x/lib/core/params.js +39 -0
- package/vendor/l5x/lib/core/result.d.ts +46 -0
- package/vendor/l5x/lib/core/result.js +21 -0
- package/vendor/l5x/lib/core/tool.d.ts +102 -0
- package/vendor/l5x/lib/core/tool.js +19 -0
- package/vendor/l5x/lib/drafts.d.ts +66 -0
- package/vendor/l5x/lib/drafts.js +98 -0
- package/vendor/l5x/lib/editing.d.ts +30 -0
- package/vendor/l5x/lib/editing.js +42 -0
- package/vendor/l5x/lib/engine/acd-edit.d.ts +13 -0
- package/vendor/l5x/lib/engine/acd-edit.js +115 -0
- package/vendor/l5x/lib/engine/analysis.d.ts +22 -0
- package/vendor/l5x/lib/engine/analysis.js +258 -0
- package/vendor/l5x/lib/engine/capabilities.d.ts +9 -0
- package/vendor/l5x/lib/engine/capabilities.js +40 -0
- package/vendor/l5x/lib/engine/change-set.d.ts +122 -0
- package/vendor/l5x/lib/engine/change-set.js +446 -0
- package/vendor/l5x/lib/engine/configuration.d.ts +21 -0
- package/vendor/l5x/lib/engine/configuration.js +42 -0
- package/vendor/l5x/lib/engine/details.d.ts +47 -0
- package/vendor/l5x/lib/engine/details.js +173 -0
- package/vendor/l5x/lib/engine/direct-polling.d.ts +91 -0
- package/vendor/l5x/lib/engine/direct-polling.js +193 -0
- package/vendor/l5x/lib/engine/download-review.d.ts +15 -0
- package/vendor/l5x/lib/engine/download-review.js +63 -0
- package/vendor/l5x/lib/engine/logic-syntax.d.ts +60 -0
- package/vendor/l5x/lib/engine/logic-syntax.js +237 -0
- package/vendor/l5x/lib/engine/observations.d.ts +41 -0
- package/vendor/l5x/lib/engine/observations.js +67 -0
- package/vendor/l5x/lib/engine/project-analysis.d.ts +131 -0
- package/vendor/l5x/lib/engine/project-analysis.js +314 -0
- package/vendor/l5x/lib/engine/review-evidence.d.ts +8 -0
- package/vendor/l5x/lib/engine/review-evidence.js +49 -0
- package/vendor/l5x/lib/engine/review.d.ts +18 -0
- package/vendor/l5x/lib/engine/review.js +123 -0
- package/vendor/l5x/lib/engine/studio-sdk.d.ts +101 -0
- package/vendor/l5x/lib/engine/studio-sdk.js +571 -0
- package/vendor/l5x/lib/engine/tag-resolution.d.ts +20 -0
- package/vendor/l5x/lib/engine/tag-resolution.js +169 -0
- package/vendor/l5x/lib/engine/usage.d.ts +16 -0
- package/vendor/l5x/lib/engine/usage.js +92 -0
- package/vendor/l5x/lib/engine/xml-regions.d.ts +23 -0
- package/vendor/l5x/lib/engine/xml-regions.js +77 -0
- package/vendor/l5x/lib/fs.d.ts +17 -0
- package/vendor/l5x/lib/fs.js +36 -0
- package/vendor/l5x/lib/index.d.ts +36 -0
- package/vendor/l5x/lib/index.js +42 -0
- package/vendor/l5x/lib/live.d.ts +48 -0
- package/vendor/l5x/lib/live.js +48 -0
- package/vendor/l5x/lib/model/aois.d.ts +39 -0
- package/vendor/l5x/lib/model/aois.js +112 -0
- package/vendor/l5x/lib/model/errors.d.ts +15 -0
- package/vendor/l5x/lib/model/errors.js +18 -0
- package/vendor/l5x/lib/model/index.d.ts +20 -0
- package/vendor/l5x/lib/model/index.js +19 -0
- package/vendor/l5x/lib/model/module.d.ts +37 -0
- package/vendor/l5x/lib/model/module.js +54 -0
- package/vendor/l5x/lib/model/modules.d.ts +24 -0
- package/vendor/l5x/lib/model/modules.js +55 -0
- package/vendor/l5x/lib/model/parse.d.ts +32 -0
- package/vendor/l5x/lib/model/parse.js +116 -0
- package/vendor/l5x/lib/model/project.d.ts +15 -0
- package/vendor/l5x/lib/model/project.js +31 -0
- package/vendor/l5x/lib/model/routines.d.ts +71 -0
- package/vendor/l5x/lib/model/routines.js +183 -0
- package/vendor/l5x/lib/model/tags.d.ts +51 -0
- package/vendor/l5x/lib/model/tags.js +119 -0
- package/vendor/l5x/lib/model/types.d.ts +304 -0
- package/vendor/l5x/lib/model/types.js +12 -0
- package/vendor/l5x/lib/model/udts.d.ts +34 -0
- package/vendor/l5x/lib/model/udts.js +81 -0
- package/vendor/l5x/lib/model/xml.d.ts +139 -0
- package/vendor/l5x/lib/model/xml.js +291 -0
- package/vendor/l5x/lib/model/xref.d.ts +28 -0
- package/vendor/l5x/lib/model/xref.js +154 -0
- package/vendor/l5x/lib/session.d.ts +119 -0
- package/vendor/l5x/lib/session.js +236 -0
- package/vendor/l5x/lib/skill.d.ts +30 -0
- package/vendor/l5x/lib/skill.js +44 -0
- package/vendor/l5x/lib/studio-state.d.ts +77 -0
- package/vendor/l5x/lib/studio-state.js +79 -0
- package/vendor/l5x/lib/studio.d.ts +29 -0
- package/vendor/l5x/lib/studio.js +40 -0
- package/vendor/l5x/lib/tools/checks.d.ts +19 -0
- package/vendor/l5x/lib/tools/checks.js +140 -0
- package/vendor/l5x/lib/tools/common.d.ts +41 -0
- package/vendor/l5x/lib/tools/common.js +84 -0
- package/vendor/l5x/lib/tools/controller.d.ts +21 -0
- package/vendor/l5x/lib/tools/controller.js +170 -0
- package/vendor/l5x/lib/tools/drafts.d.ts +23 -0
- package/vendor/l5x/lib/tools/drafts.js +270 -0
- package/vendor/l5x/lib/tools/live.d.ts +21 -0
- package/vendor/l5x/lib/tools/live.js +175 -0
- package/vendor/l5x/lib/tools/read.d.ts +19 -0
- package/vendor/l5x/lib/tools/read.js +245 -0
- package/vendor/l5x/lib/tools/references.d.ts +20 -0
- package/vendor/l5x/lib/tools/references.js +269 -0
- package/vendor/l5x/lib/tools/studio.d.ts +21 -0
- package/vendor/l5x/lib/tools/studio.js +193 -0
- package/vendor/l5x/poll-worker.mjs +77 -0
- package/vendor/l5x/studio-bridge/NuGet.Config +1 -0
- package/vendor/l5x/studio-bridge/Program.cs +304 -0
- package/vendor/l5x/studio-bridge/StudioBridge.csproj +11 -0
- package/vendor/mwi/README.md +60 -0
- package/vendor/mwi/SKILL.md +87 -0
- package/vendor/mwi/bridge/README.md +8 -0
- package/vendor/mwi/bridge/mw_bridge.ps1 +2763 -0
- package/vendor/mwi/bridge/sdk-interop-importer.cs +30 -0
- package/vendor/mwi/bridge/sdk-variable-reader.cs +22 -0
- package/vendor/mwi/bridge/start_bridge.cmd +21 -0
- package/vendor/mwi/bridge/watch_dialogs.cmd +13 -0
- package/vendor/mwi/bridge/watch_dialogs.ps1 +153 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__init__.py +14 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/__init__.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/block_interfaces.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/cfb.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/datatypes.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/declarations.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/errors.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/graphical_listing.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/grid.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/knowledge.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/mwt_bind.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/navigation.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/program_checks.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/project.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/staging.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/structure.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/task_bindings.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/tasks.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/tree.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/validation.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/variable_edit.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/variables.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/workflow.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/block_interfaces.py +275 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/build.py +339 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/capture.py +448 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/cfb.py +765 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/com.py +268 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/datatypes.py +204 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/declarations.py +399 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/descriptions.py +30 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/eip.py +292 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/errors.py +36 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/graphical_listing.py +208 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/grid.py +227 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/ide.py +317 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/iec.py +317 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/installed_help.py +151 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/knowledge.py +218 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/libraries.py +250 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/manuals.py +520 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/mwt_bind.py +133 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/navigation.py +44 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/pou_writer.py +1547 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/program_checks.py +360 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/project.py +704 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/reference_data/catalog.json +672 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/reference_data/patterns.json +86 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/restore.py +208 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/server.py +1913 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/snapshot.py +138 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/st_comments.py +25 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/staging.py +179 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/stlint.py +676 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/structure.py +98 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/task_bindings.py +68 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/tasks.py +162 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/transaction.py +157 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/tree.py +563 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/tree_writer.py +790 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/uia.py +465 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/validation.py +41 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/variable_edit.py +183 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/variables.py +472 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/workflow.py +130 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/writer.py +666 -0
- package/vendor/mwi/code/mw_code.py +1258 -0
- package/vendor/mwi/legacy/edit-session.js +50 -0
- package/vendor/mwi/legacy/fb-insertion.js +74 -0
- package/vendor/mwi/legacy/native-code.js +83 -0
- package/vendor/mwi/legacy/native-groups.js +75 -0
- package/vendor/mwi/legacy/native-structure.js +143 -0
- package/vendor/mwi/legacy/native-variables.js +77 -0
- package/vendor/mwi/legacy/pou-conversion.js +47 -0
- package/vendor/mwi/legacy/pou-package.js +66 -0
- package/vendor/mwi/lib/core/bridge.d.ts +71 -0
- package/vendor/mwi/lib/core/bridge.js +53 -0
- package/vendor/mwi/lib/core/code-engine.d.ts +47 -0
- package/vendor/mwi/lib/core/code-engine.js +109 -0
- package/vendor/mwi/lib/core/errors.d.ts +37 -0
- package/vendor/mwi/lib/core/errors.js +47 -0
- package/vendor/mwi/lib/core/params.d.ts +52 -0
- package/vendor/mwi/lib/core/params.js +46 -0
- package/vendor/mwi/lib/core/ps-bridge.d.ts +62 -0
- package/vendor/mwi/lib/core/ps-bridge.js +127 -0
- package/vendor/mwi/lib/core/result.d.ts +45 -0
- package/vendor/mwi/lib/core/result.js +20 -0
- package/vendor/mwi/lib/core/tool.d.ts +80 -0
- package/vendor/mwi/lib/core/tool.js +55 -0
- package/vendor/mwi/lib/edit/compare.d.ts +33 -0
- package/vendor/mwi/lib/edit/compare.js +70 -0
- package/vendor/mwi/lib/edit.d.ts +20 -0
- package/vendor/mwi/lib/edit.js +32 -0
- package/vendor/mwi/lib/index.d.ts +21 -0
- package/vendor/mwi/lib/index.js +33 -0
- package/vendor/mwi/lib/read/service.d.ts +117 -0
- package/vendor/mwi/lib/read/service.js +229 -0
- package/vendor/mwi/lib/read.d.ts +21 -0
- package/vendor/mwi/lib/read.js +33 -0
- package/vendor/mwi/lib/session/project.d.ts +34 -0
- package/vendor/mwi/lib/session/project.js +54 -0
- package/vendor/mwi/lib/session/service.d.ts +167 -0
- package/vendor/mwi/lib/session/service.js +566 -0
- package/vendor/mwi/lib/session/stage.d.ts +84 -0
- package/vendor/mwi/lib/session/stage.js +187 -0
- package/vendor/mwi/lib/session/workspace.d.ts +52 -0
- package/vendor/mwi/lib/session/workspace.js +90 -0
- package/vendor/mwi/lib/skill.d.ts +36 -0
- package/vendor/mwi/lib/skill.js +56 -0
- package/vendor/mwi/lib/tools/edit.d.ts +20 -0
- package/vendor/mwi/lib/tools/edit.js +286 -0
- package/vendor/mwi/lib/tools/read.d.ts +19 -0
- package/vendor/mwi/lib/tools/read.js +161 -0
- package/vendor/mwi/lib/tools/session.d.ts +20 -0
- package/vendor/mwi/lib/tools/session.js +166 -0
- package/vendor/mwi/lib/tools/verify.d.ts +19 -0
- package/vendor/mwi/lib/tools/verify.js +85 -0
- package/vendor/mwi/lib/verify.d.ts +17 -0
- package/vendor/mwi/lib/verify.js +29 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Human approval for the controller actions that change a machine.
|
|
3
|
+
*
|
|
4
|
+
* Reading a controller needs no approval. Uploading a snapshot, changing mode
|
|
5
|
+
* and downloading a project each ask the session's approval service for this
|
|
6
|
+
* exact operation, and proceed only on `allowed-once`. A session whose policy
|
|
7
|
+
* is not `ask` cannot request approval at all, and the tool refuses before it
|
|
8
|
+
* touches the controller.
|
|
9
|
+
*
|
|
10
|
+
* @module dsh-engineering-l5x/approvals
|
|
11
|
+
*/
|
|
12
|
+
import { L5xError } from "./core/errors.js";
|
|
13
|
+
/** The calling session behind an agent context. */
|
|
14
|
+
function sessionOf(exec) {
|
|
15
|
+
return exec?.agent?.session;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Report whether this session can request per-operation approval.
|
|
19
|
+
*
|
|
20
|
+
* @param ctx - Plugin context that may provide the approval service.
|
|
21
|
+
* @param exec - Calling context carrying the agent.
|
|
22
|
+
* @returns The status fields the tool shows the reader.
|
|
23
|
+
*/
|
|
24
|
+
export function approvalStatus(ctx, exec) {
|
|
25
|
+
const approval = ctx.get?.('approval');
|
|
26
|
+
const session = sessionOf(exec);
|
|
27
|
+
const presets = ctx.get?.('permissionPresets');
|
|
28
|
+
const preset = session === undefined ? undefined : presets?.current?.(session);
|
|
29
|
+
const policy = session === undefined || approval === undefined
|
|
30
|
+
? undefined
|
|
31
|
+
: approval.overrideOf?.(session) ?? approval.config?.policy ?? 'ask';
|
|
32
|
+
const requestable = session !== undefined && typeof approval?.request === 'function' && policy === 'ask';
|
|
33
|
+
return {
|
|
34
|
+
permission_preset: preset ?? 'unknown',
|
|
35
|
+
approval_policy: policy ?? 'unknown',
|
|
36
|
+
approval_service: typeof approval?.request === 'function',
|
|
37
|
+
agent_context: session !== undefined,
|
|
38
|
+
approved_controller_actions_requestable: requestable,
|
|
39
|
+
approval_required_per_call: ['capture_controller_snapshot', 'change_controller_mode', 'download_project'],
|
|
40
|
+
...(requestable
|
|
41
|
+
? { note: 'A human must still approve each exact operation; this does not confirm an answerer is online.' }
|
|
42
|
+
: {
|
|
43
|
+
next_step: policy === 'never'
|
|
44
|
+
? preset === 'danger-full-access'
|
|
45
|
+
? 'Switch this session from Full access to a preset that asks for approval, then call this tool again.'
|
|
46
|
+
: 'This session has Never approval policy. Change it to Ask before a controller action, then call this tool again.'
|
|
47
|
+
: 'Use a session with an agent context and an approval service that can ask. Do not bypass the plugin.',
|
|
48
|
+
}),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Refuse when approval cannot be requested at all.
|
|
53
|
+
*
|
|
54
|
+
* @param ctx - Plugin context.
|
|
55
|
+
* @param exec - Calling context.
|
|
56
|
+
*/
|
|
57
|
+
export function ensureApprovalReady(ctx, exec) {
|
|
58
|
+
const status = approvalStatus(ctx, exec);
|
|
59
|
+
if (!status.approved_controller_actions_requestable) {
|
|
60
|
+
throw new L5xError('APPROVAL_UNAVAILABLE', `Human approval cannot be requested (policy: ${status.approval_policy}). ${status.next_step ?? ''}`, 'Switch to an approval preset that asks, then call this tool again.');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Ask for approval of one exact operation and refuse unless it is granted once.
|
|
65
|
+
*
|
|
66
|
+
* @param ctx - Plugin context.
|
|
67
|
+
* @param exec - Calling context.
|
|
68
|
+
* @param toolName - Tool asking, recorded with the request.
|
|
69
|
+
* @param reason - Exact operation the human is approving.
|
|
70
|
+
*/
|
|
71
|
+
export async function requireHumanApproval(ctx, exec, toolName, reason) {
|
|
72
|
+
ensureApprovalReady(ctx, exec);
|
|
73
|
+
const approval = ctx.get?.('approval');
|
|
74
|
+
if (exec?.agent === undefined || typeof approval?.request !== 'function') {
|
|
75
|
+
throw new L5xError('APPROVAL_UNAVAILABLE', `${toolName} requires human approval for this exact operation.`, 'Use a session with an agent context and an approval service, then call this tool again.');
|
|
76
|
+
}
|
|
77
|
+
let outcome;
|
|
78
|
+
try {
|
|
79
|
+
outcome = await approval.request({
|
|
80
|
+
agent: exec.agent,
|
|
81
|
+
toolName,
|
|
82
|
+
callId: exec.callId,
|
|
83
|
+
reason,
|
|
84
|
+
...(exec.signal === undefined ? {} : { signal: exec.signal }),
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
throw new L5xError('APPROVAL_UNAVAILABLE', error instanceof Error ? error.message : String(error), 'Check that a human can answer approval requests in this session, then retry.');
|
|
89
|
+
}
|
|
90
|
+
if (outcome !== 'allowed-once') {
|
|
91
|
+
throw new L5xError('APPROVAL_DENIED', `${toolName} was not approved (${outcome}).`, 'Nothing was sent to the controller. Ask the human what to change, then request a new operation.');
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "@aryacode/dsh-engineering-l5x",
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
var module = { exports: {} };
|
|
5
|
+
var exports = module.exports;
|
|
6
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
+
let react = require("react");
|
|
8
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
9
|
+
// src/client/locales.ts
|
|
10
|
+
/** Copy owned by the Studio 5000 loader console. */
|
|
11
|
+
/** Locale namespace this console registers its dictionaries under. */
|
|
12
|
+
const NS = "studio5000";
|
|
13
|
+
/** Chinese copy. */
|
|
14
|
+
const zh = {
|
|
15
|
+
"trend.title": "PLC 实时趋势",
|
|
16
|
+
"trend.actualInterval": "显示窗口平均实际间隔",
|
|
17
|
+
"trend.overruns": "观察到的超时周期",
|
|
18
|
+
"trend.badValue": "读数不可用",
|
|
19
|
+
"trend.connecting": "正在连接…",
|
|
20
|
+
"trend.running": "采集中",
|
|
21
|
+
"trend.completed": "采集完成",
|
|
22
|
+
"trend.stopped": "已停止",
|
|
23
|
+
"trend.failed": "采集失败",
|
|
24
|
+
"trend.unavailable": "实时趋势流不可用",
|
|
25
|
+
"trend.gap": "部分历史数据不可用",
|
|
26
|
+
"trend.stopFailed": "无法停止采集,请重试",
|
|
27
|
+
"trend.stop": "停止",
|
|
28
|
+
"trend.interval": "请求间隔",
|
|
29
|
+
"trend.samples": "显示采样数",
|
|
30
|
+
"trend.source": "源文件",
|
|
31
|
+
"trend.limitation": "读取并非 PLC 扫描同步,可能遗漏变化;尚未验证在线逻辑与导出一致",
|
|
32
|
+
title: "Studio 5000",
|
|
33
|
+
workspace: "工作区",
|
|
34
|
+
refresh: "刷新",
|
|
35
|
+
rescan: "重新扫描",
|
|
36
|
+
recent: "最近打开",
|
|
37
|
+
projects: "项目",
|
|
38
|
+
open: "在 Studio 5000 中打开",
|
|
39
|
+
filter: "筛选 ACD 项目",
|
|
40
|
+
"fallback.open": "使用 Windows 默认应用打开",
|
|
41
|
+
"fallback.description": "文件处理程序查询不可用或未找到 Studio。此操作将使用此 ACD 的系统默认应用。",
|
|
42
|
+
"status.defaultOpened": "已发送给系统默认应用。请确认打开的是正确项目。",
|
|
43
|
+
"launch.description": "在已安装的 Studio 5000 中打开项目。",
|
|
44
|
+
"status.desktop": "此主机没有可用的桌面。",
|
|
45
|
+
"status.discovery": "无法检查此文件的应用程序。请更新主机或检查连接。",
|
|
46
|
+
"status.handler": "没有找到 Studio 5000 文件处理程序。请检查安装和 ACD 文件关联。",
|
|
47
|
+
reveal: "在资源管理器中显示",
|
|
48
|
+
"empty.none": "此工作区中没有找到 .acd 文件。",
|
|
49
|
+
"empty.scanning": "正在扫描工作区…",
|
|
50
|
+
"empty.noSession": "此工作区还没有会话,无法列出文件。",
|
|
51
|
+
"empty.noWorkspace": "还没有工作区。请先打开一个项目文件夹。",
|
|
52
|
+
"unavailable.listing": "此配置未提供工作区文件列表功能。",
|
|
53
|
+
"unavailable.launch": "此主机无法打开本机文件。",
|
|
54
|
+
"unavailable.desktop": "当前主机没有可用的桌面程序。",
|
|
55
|
+
"status.opened": "已发送打开请求。请在 Studio 5000 中确认项目已打开。",
|
|
56
|
+
"status.revealed": "已在资源管理器中显示。",
|
|
57
|
+
"status.failed": "打开失败。",
|
|
58
|
+
"status.scanFailed": "部分目录无法读取。",
|
|
59
|
+
"status.truncated": "列表已被截断。",
|
|
60
|
+
size: "大小",
|
|
61
|
+
truncated: "(已截断)"
|
|
62
|
+
};
|
|
63
|
+
/** English copy, same keys. */
|
|
64
|
+
const en = {
|
|
65
|
+
"trend.title": "Live PLC trend",
|
|
66
|
+
"trend.actualInterval": "Mean actual interval in displayed window",
|
|
67
|
+
"trend.overruns": "Observed sweep overruns",
|
|
68
|
+
"trend.badValue": "Reading unavailable",
|
|
69
|
+
"trend.connecting": "Connecting…",
|
|
70
|
+
"trend.running": "Collecting",
|
|
71
|
+
"trend.completed": "Capture complete",
|
|
72
|
+
"trend.stopped": "Stopped",
|
|
73
|
+
"trend.failed": "Capture failed",
|
|
74
|
+
"trend.unavailable": "Live trend stream unavailable",
|
|
75
|
+
"trend.gap": "Some history is unavailable",
|
|
76
|
+
"trend.stopFailed": "Could not stop capture. Try again.",
|
|
77
|
+
"trend.stop": "Stop",
|
|
78
|
+
"trend.interval": "Requested interval",
|
|
79
|
+
"trend.samples": "Displayed samples",
|
|
80
|
+
"trend.source": "Source export",
|
|
81
|
+
"trend.limitation": "Reads are not scan-synchronized and may miss transitions. Online logic matching this export is unverified.",
|
|
82
|
+
title: "Studio 5000",
|
|
83
|
+
workspace: "Workspace",
|
|
84
|
+
refresh: "Refresh",
|
|
85
|
+
rescan: "Rescan",
|
|
86
|
+
recent: "Recently opened",
|
|
87
|
+
projects: "Projects",
|
|
88
|
+
open: "Open in Studio 5000",
|
|
89
|
+
filter: "Filter ACD projects",
|
|
90
|
+
"fallback.open": "Open with Windows default",
|
|
91
|
+
"fallback.description": "Handler discovery is unavailable or found no Studio handler. This opens the selected ACD using its system default application.",
|
|
92
|
+
"status.defaultOpened": "Sent to the system default application. Confirm the correct project opened.",
|
|
93
|
+
"launch.description": "Open projects in installed Studio 5000.",
|
|
94
|
+
"status.desktop": "No desktop is available on this host.",
|
|
95
|
+
"status.discovery": "Could not check file handlers. Update the host or check the connection.",
|
|
96
|
+
"status.handler": "No Studio 5000 file handler found. Check the installation and ACD file association.",
|
|
97
|
+
reveal: "Reveal in Explorer",
|
|
98
|
+
"empty.none": "No .acd files found in this workspace.",
|
|
99
|
+
"empty.scanning": "Scanning the workspace…",
|
|
100
|
+
"empty.noSession": "This workspace has no Session yet, so its files cannot be listed.",
|
|
101
|
+
"empty.noWorkspace": "No workspace yet. Open a project folder first.",
|
|
102
|
+
"unavailable.listing": "Workspace file listing is not available in this profile.",
|
|
103
|
+
"unavailable.launch": "This host cannot open local files.",
|
|
104
|
+
"unavailable.desktop": "No desktop application is available on this host.",
|
|
105
|
+
"status.opened": "Open request sent. Confirm the project opened in Studio 5000.",
|
|
106
|
+
"status.revealed": "Revealed in the file manager.",
|
|
107
|
+
"status.failed": "Could not open the project.",
|
|
108
|
+
"status.scanFailed": "Some directories could not be read.",
|
|
109
|
+
"status.truncated": "The listing was truncated.",
|
|
110
|
+
size: "Size",
|
|
111
|
+
truncated: "(truncated)"
|
|
112
|
+
};
|
|
113
|
+
// src/client/trend-model.ts
|
|
114
|
+
/** Parse a recorded start receipt. @param content - Logged tool content. @returns Valid receipt or undefined. */
|
|
115
|
+
function trendReceipt(content) {
|
|
116
|
+
if (!Array.isArray(content)) return void 0;
|
|
117
|
+
for (const entry of content) {
|
|
118
|
+
if (!entry || typeof entry !== "object" || entry.type !== "text" || typeof entry.text !== "string") continue;
|
|
119
|
+
try {
|
|
120
|
+
const value = JSON.parse(entry.text);
|
|
121
|
+
if (value && typeof value === "object" && "view" in value && value.view === "plc-trend" && "job_id" in value && typeof value.job_id === "string" && /^plc-trend-\d+$/u.test(value.job_id) && "owner_session_id" in value && typeof value.owner_session_id === "string" && "requested_interval_ms" in value && typeof value.requested_interval_ms === "number" && "source_sha256" in value && typeof value.source_sha256 === "string" && "display_refresh_ms" in value && typeof value.display_refresh_ms === "number" && value.display_refresh_ms >= 50 && value.display_refresh_ms <= 1e4 && "display_max_samples" in value && typeof value.display_max_samples === "number" && value.display_max_samples >= 2 && value.display_max_samples <= 2e4) return {
|
|
122
|
+
jobId: value.job_id,
|
|
123
|
+
sessionId: value.owner_session_id,
|
|
124
|
+
interval: value.requested_interval_ms,
|
|
125
|
+
sourceHash: value.source_sha256,
|
|
126
|
+
refresh: value.display_refresh_ms,
|
|
127
|
+
maxSamples: value.display_max_samples
|
|
128
|
+
};
|
|
129
|
+
} catch (error) {}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/** Parse one sampler line. @param line - Retained job output. @returns Plot point or undefined for other frames. */
|
|
133
|
+
function trendPoint(line) {
|
|
134
|
+
const value = JSON.parse(line);
|
|
135
|
+
if (!value || typeof value !== "object" || !("type" in value) || value.type !== "sample") return void 0;
|
|
136
|
+
if (!("elapsed_ms" in value) || typeof value.elapsed_ms !== "number" || !Number.isFinite(value.elapsed_ms) || !("values" in value) || !Array.isArray(value.values)) throw new Error("Invalid trend sample");
|
|
137
|
+
const values = value.values.map((item) => {
|
|
138
|
+
if (!item || typeof item !== "object" || !("tag" in item) || typeof item.tag !== "string" || !("quality" in item) || typeof item.quality !== "string" || !("value" in item) || !(item.value === null || typeof item.value === "boolean" || typeof item.value === "number" && Number.isFinite(item.value))) throw new Error("Invalid trend value");
|
|
139
|
+
return {
|
|
140
|
+
tag: item.tag,
|
|
141
|
+
quality: item.quality,
|
|
142
|
+
value: item.value
|
|
143
|
+
};
|
|
144
|
+
});
|
|
145
|
+
return {
|
|
146
|
+
elapsed: value.elapsed_ms,
|
|
147
|
+
overrun: "overrun" in value && typeof value.overrun === "boolean" ? value.overrun : void 0,
|
|
148
|
+
values
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
/** Build distinct line segments so bad samples do not imply continuity. @param points - Retained samples. @param tag - Scoped tag. @returns SVG line segments and range. */
|
|
152
|
+
function trendSegments(points, tag) {
|
|
153
|
+
const finite = points.flatMap((point) => point.values.filter((item) => item.tag === tag && item.quality === "good" && item.value !== null).map((item) => Number(item.value)));
|
|
154
|
+
const min = Math.min(0, ...finite);
|
|
155
|
+
const max = Math.max(1, ...finite);
|
|
156
|
+
const start = points[0]?.elapsed ?? 0;
|
|
157
|
+
const duration = Math.max(1, (points.at(-1)?.elapsed ?? start) - start);
|
|
158
|
+
const segments = [];
|
|
159
|
+
let current = [];
|
|
160
|
+
for (const point of points) {
|
|
161
|
+
const value = point.values.find((item) => item.tag === tag);
|
|
162
|
+
if (!value || value.quality !== "good" || value.value === null) {
|
|
163
|
+
if (current.length) segments.push(current.join(" "));
|
|
164
|
+
current = [];
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
const x = 10 + (point.elapsed - start) / duration * 580;
|
|
168
|
+
const y = 100 - (Number(value.value) - min) / (max - min) * 90;
|
|
169
|
+
if (typeof value.value === "boolean" && current.length) current.push(`${x},${current.at(-1)?.split(",")[1]}`);
|
|
170
|
+
current.push(`${x},${y}`);
|
|
171
|
+
}
|
|
172
|
+
if (current.length) segments.push(current.join(" "));
|
|
173
|
+
return {
|
|
174
|
+
segments,
|
|
175
|
+
min,
|
|
176
|
+
max
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
// src/client/TrendCard.tsx
|
|
180
|
+
/** Inline live PLC trend driven by the existing session-authorized job stream. */
|
|
181
|
+
/** Display a live watch and stop it without cancelling the chat. @param props - Tool seat and existing job Remote. @returns Trend card. */
|
|
182
|
+
function TrendCard({ phase, block, trendJobs, t }) {
|
|
183
|
+
const receipt = phase === "result" ? trendReceipt(block.content) : void 0;
|
|
184
|
+
const [points, setPoints] = (0, react.useState)([]);
|
|
185
|
+
const [state, setState] = (0, react.useState)("trend.connecting");
|
|
186
|
+
const [stopping, setStopping] = (0, react.useState)(false);
|
|
187
|
+
(0, react.useEffect)(() => {
|
|
188
|
+
if (!receipt || !trendJobs) return;
|
|
189
|
+
const abort = new AbortController();
|
|
190
|
+
let pending = [];
|
|
191
|
+
let dirty = false;
|
|
192
|
+
let tail = "";
|
|
193
|
+
const flush = () => {
|
|
194
|
+
if (!dirty || abort.signal.aborted) return;
|
|
195
|
+
dirty = false;
|
|
196
|
+
setPoints([...pending]);
|
|
197
|
+
};
|
|
198
|
+
const timer = setInterval(flush, receipt.refresh);
|
|
199
|
+
const run = async () => {
|
|
200
|
+
try {
|
|
201
|
+
for await (const frame of trendJobs.follow({
|
|
202
|
+
jobId: receipt.jobId,
|
|
203
|
+
sessionId: receipt.sessionId
|
|
204
|
+
}, abort.signal)) {
|
|
205
|
+
if (abort.signal.aborted) return;
|
|
206
|
+
if (!frame || typeof frame !== "object" || !("type" in frame)) throw new Error("Invalid job frame");
|
|
207
|
+
if (frame.type === "opened") setState("trend.running");
|
|
208
|
+
if (frame.type === "output" && "chunks" in frame && Array.isArray(frame.chunks)) {
|
|
209
|
+
if ("lossy" in frame && frame.lossy === true) {
|
|
210
|
+
pending = [];
|
|
211
|
+
tail = "";
|
|
212
|
+
setState("trend.gap");
|
|
213
|
+
}
|
|
214
|
+
for (const chunk of frame.chunks) {
|
|
215
|
+
if (!chunk || typeof chunk !== "object" || typeof chunk.text !== "string") throw new Error("Invalid trend output");
|
|
216
|
+
if (chunk.gapBefore === true) {
|
|
217
|
+
pending = [];
|
|
218
|
+
tail = "";
|
|
219
|
+
setState("trend.gap");
|
|
220
|
+
}
|
|
221
|
+
tail += chunk.text;
|
|
222
|
+
const lines = tail.split("\n");
|
|
223
|
+
tail = lines.pop() ?? "";
|
|
224
|
+
for (const line of lines) {
|
|
225
|
+
const point = trendPoint(line);
|
|
226
|
+
if (point) {
|
|
227
|
+
pending.push(point);
|
|
228
|
+
if (pending.length > receipt.maxSamples) pending.shift();
|
|
229
|
+
dirty = true;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if (frame.type === "status" && "job" in frame && frame.job && typeof frame.job === "object" && "status" in frame.job) {
|
|
235
|
+
flush();
|
|
236
|
+
setState(frame.job.status === "completed" ? "trend.completed" : frame.job.status === "killed" ? "trend.stopped" : "trend.failed");
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
} catch (error) {
|
|
240
|
+
if (!abort.signal.aborted) setState("trend.unavailable");
|
|
241
|
+
} finally {
|
|
242
|
+
clearInterval(timer);
|
|
243
|
+
flush();
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
run();
|
|
247
|
+
return () => {
|
|
248
|
+
abort.abort();
|
|
249
|
+
clearInterval(timer);
|
|
250
|
+
};
|
|
251
|
+
}, [
|
|
252
|
+
receipt?.jobId,
|
|
253
|
+
receipt?.sessionId,
|
|
254
|
+
receipt?.refresh,
|
|
255
|
+
receipt?.maxSamples,
|
|
256
|
+
trendJobs
|
|
257
|
+
]);
|
|
258
|
+
if (!receipt) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
259
|
+
role: "status",
|
|
260
|
+
children: t(block.isError ? "trend.failed" : "trend.connecting")
|
|
261
|
+
});
|
|
262
|
+
const tags = [...new Set(points.flatMap((point) => point.values.map((value) => value.tag)))];
|
|
263
|
+
const stop = async () => {
|
|
264
|
+
if (!trendJobs || stopping) return;
|
|
265
|
+
setStopping(true);
|
|
266
|
+
try {
|
|
267
|
+
if (!(await trendJobs.kill({
|
|
268
|
+
jobId: receipt.jobId,
|
|
269
|
+
sessionId: receipt.sessionId
|
|
270
|
+
})).ok) setState("trend.stopFailed");
|
|
271
|
+
} catch (error) {
|
|
272
|
+
setState("trend.stopFailed");
|
|
273
|
+
} finally {
|
|
274
|
+
setStopping(false);
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
278
|
+
"aria-label": t("trend.title"),
|
|
279
|
+
style: {
|
|
280
|
+
padding: "12px",
|
|
281
|
+
border: "1px solid var(--dsw-alias-border-l3)",
|
|
282
|
+
borderRadius: "8px"
|
|
283
|
+
},
|
|
284
|
+
children: [
|
|
285
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
286
|
+
style: {
|
|
287
|
+
display: "flex",
|
|
288
|
+
gap: "12px",
|
|
289
|
+
alignItems: "center"
|
|
290
|
+
},
|
|
291
|
+
children: [
|
|
292
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("trend.title") }),
|
|
293
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
294
|
+
role: "status",
|
|
295
|
+
children: t(trendJobs ? state : "trend.unavailable")
|
|
296
|
+
}),
|
|
297
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
298
|
+
type: "button",
|
|
299
|
+
style: {
|
|
300
|
+
color: "var(--dsw-alias-label-primary)",
|
|
301
|
+
background: "transparent",
|
|
302
|
+
border: "1px solid var(--dsw-alias-border-l3)",
|
|
303
|
+
borderRadius: "6px",
|
|
304
|
+
font: "inherit",
|
|
305
|
+
padding: "4px 10px"
|
|
306
|
+
},
|
|
307
|
+
disabled: !trendJobs || stopping || [
|
|
308
|
+
"trend.completed",
|
|
309
|
+
"trend.stopped",
|
|
310
|
+
"trend.failed"
|
|
311
|
+
].includes(state),
|
|
312
|
+
onClick: () => {
|
|
313
|
+
stop();
|
|
314
|
+
},
|
|
315
|
+
children: t("trend.stop")
|
|
316
|
+
})
|
|
317
|
+
]
|
|
318
|
+
}),
|
|
319
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", { children: [
|
|
320
|
+
t("trend.interval"),
|
|
321
|
+
": ",
|
|
322
|
+
receipt.interval,
|
|
323
|
+
" ms · ",
|
|
324
|
+
t("trend.samples"),
|
|
325
|
+
": ",
|
|
326
|
+
points.length
|
|
327
|
+
] }),
|
|
328
|
+
points.length > 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", { children: [
|
|
329
|
+
t("trend.actualInterval"),
|
|
330
|
+
": ",
|
|
331
|
+
((points.at(-1).elapsed - points[0].elapsed) / (points.length - 1)).toFixed(1),
|
|
332
|
+
" ms · ",
|
|
333
|
+
t("trend.overruns"),
|
|
334
|
+
": ",
|
|
335
|
+
points.filter((point) => point.overrun === true).length
|
|
336
|
+
] }),
|
|
337
|
+
tags.map((tag) => {
|
|
338
|
+
const plot = trendSegments(points, tag);
|
|
339
|
+
const latest = points.at(-1)?.values.find((value) => value.tag === tag);
|
|
340
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("figure", {
|
|
341
|
+
style: { margin: "12px 0" },
|
|
342
|
+
children: [
|
|
343
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("figcaption", { children: [
|
|
344
|
+
tag,
|
|
345
|
+
" · ",
|
|
346
|
+
latest?.quality === "good" ? String(latest.value) : t("trend.badValue"),
|
|
347
|
+
" · ",
|
|
348
|
+
plot.min,
|
|
349
|
+
"–",
|
|
350
|
+
plot.max
|
|
351
|
+
] }),
|
|
352
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
353
|
+
viewBox: "0 0 600 110",
|
|
354
|
+
role: "img",
|
|
355
|
+
"aria-label": tag,
|
|
356
|
+
style: {
|
|
357
|
+
width: "100%",
|
|
358
|
+
height: "110px"
|
|
359
|
+
},
|
|
360
|
+
children: plot.segments.map((segment, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("polyline", {
|
|
361
|
+
points: segment,
|
|
362
|
+
fill: "none",
|
|
363
|
+
stroke: "currentColor",
|
|
364
|
+
strokeWidth: "1.5"
|
|
365
|
+
}, index))
|
|
366
|
+
}),
|
|
367
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("small", { children: [
|
|
368
|
+
((points[0]?.elapsed ?? 0) / 1e3).toFixed(2),
|
|
369
|
+
"–",
|
|
370
|
+
((points.at(-1)?.elapsed ?? 0) / 1e3).toFixed(2),
|
|
371
|
+
" s"
|
|
372
|
+
] })
|
|
373
|
+
]
|
|
374
|
+
}, tag);
|
|
375
|
+
}),
|
|
376
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("small", { children: [
|
|
377
|
+
t("trend.source"),
|
|
378
|
+
": ",
|
|
379
|
+
receipt.sourceHash.slice(0, 12),
|
|
380
|
+
" · ",
|
|
381
|
+
t("trend.limitation")
|
|
382
|
+
] })
|
|
383
|
+
]
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
// src/client/index.ts
|
|
387
|
+
/** Required client services; job availability is explained inside the card. */
|
|
388
|
+
const inject = [
|
|
389
|
+
"slots",
|
|
390
|
+
"locale",
|
|
391
|
+
"remote"
|
|
392
|
+
];
|
|
393
|
+
/** Register the live trend card without a Studio launcher sidebar. @param ctx - Browser service context. */
|
|
394
|
+
function apply(ctx) {
|
|
395
|
+
ctx.effect(() => ctx.locale.register(NS, {
|
|
396
|
+
zh,
|
|
397
|
+
en
|
|
398
|
+
}), "l5x-live: trend copy");
|
|
399
|
+
ctx.slots.inject("tool.call.toolview", () => ctx.slots.register({
|
|
400
|
+
name: "tool.call.toolview",
|
|
401
|
+
key: "start_plc_trend",
|
|
402
|
+
locale: NS,
|
|
403
|
+
inject: () => ({ trendJobs: ctx.remote.job })
|
|
404
|
+
}, TrendCard));
|
|
405
|
+
}
|
|
406
|
+
exports.apply = apply;
|
|
407
|
+
exports.inject = inject;
|
|
408
|
+
return module.exports;
|
|
409
|
+
}
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","names":["useState"],"sources":["../src/client/locales.ts","../src/client/trend-model.ts","../src/client/TrendCard.tsx","../src/client/index.ts"],"sourcesContent":["/** Copy owned by the Studio 5000 loader console. */\n\n/** Locale namespace this console registers its dictionaries under. */\nexport const NS = 'studio5000'\n\n/** Chinese copy. */\nexport const zh = {\n 'trend.title': 'PLC 实时趋势',\n 'trend.actualInterval': '显示窗口平均实际间隔',\n 'trend.overruns': '观察到的超时周期',\n 'trend.badValue': '读数不可用',\n 'trend.connecting': '正在连接…',\n 'trend.running': '采集中',\n 'trend.completed': '采集完成',\n 'trend.stopped': '已停止',\n 'trend.failed': '采集失败',\n 'trend.unavailable': '实时趋势流不可用',\n 'trend.gap': '部分历史数据不可用',\n 'trend.stopFailed': '无法停止采集,请重试',\n 'trend.stop': '停止',\n 'trend.interval': '请求间隔',\n 'trend.samples': '显示采样数',\n 'trend.source': '源文件',\n 'trend.limitation': '读取并非 PLC 扫描同步,可能遗漏变化;尚未验证在线逻辑与导出一致',\n title: 'Studio 5000',\n workspace: '工作区',\n refresh: '刷新',\n rescan: '重新扫描',\n recent: '最近打开',\n projects: '项目',\n open: '在 Studio 5000 中打开',\n filter: '筛选 ACD 项目',\n 'fallback.open': '使用 Windows 默认应用打开',\n 'fallback.description': '文件处理程序查询不可用或未找到 Studio。此操作将使用此 ACD 的系统默认应用。',\n 'status.defaultOpened': '已发送给系统默认应用。请确认打开的是正确项目。',\n 'launch.description': '在已安装的 Studio 5000 中打开项目。',\n 'status.desktop': '此主机没有可用的桌面。',\n 'status.discovery': '无法检查此文件的应用程序。请更新主机或检查连接。',\n 'status.handler': '没有找到 Studio 5000 文件处理程序。请检查安装和 ACD 文件关联。',\n reveal: '在资源管理器中显示',\n 'empty.none': '此工作区中没有找到 .acd 文件。',\n 'empty.scanning': '正在扫描工作区…',\n 'empty.noSession': '此工作区还没有会话,无法列出文件。',\n 'empty.noWorkspace': '还没有工作区。请先打开一个项目文件夹。',\n 'unavailable.listing': '此配置未提供工作区文件列表功能。',\n 'unavailable.launch': '此主机无法打开本机文件。',\n 'unavailable.desktop': '当前主机没有可用的桌面程序。',\n 'status.opened': '已发送打开请求。请在 Studio 5000 中确认项目已打开。',\n 'status.revealed': '已在资源管理器中显示。',\n 'status.failed': '打开失败。',\n 'status.scanFailed': '部分目录无法读取。',\n 'status.truncated': '列表已被截断。',\n size: '大小',\n truncated: '(已截断)',\n} satisfies Record<string, string>\n\n/** English copy, same keys. */\nexport const en = {\n 'trend.title': 'Live PLC trend',\n 'trend.actualInterval': 'Mean actual interval in displayed window',\n 'trend.overruns': 'Observed sweep overruns',\n 'trend.badValue': 'Reading unavailable',\n 'trend.connecting': 'Connecting…',\n 'trend.running': 'Collecting',\n 'trend.completed': 'Capture complete',\n 'trend.stopped': 'Stopped',\n 'trend.failed': 'Capture failed',\n 'trend.unavailable': 'Live trend stream unavailable',\n 'trend.gap': 'Some history is unavailable',\n 'trend.stopFailed': 'Could not stop capture. Try again.',\n 'trend.stop': 'Stop',\n 'trend.interval': 'Requested interval',\n 'trend.samples': 'Displayed samples',\n 'trend.source': 'Source export',\n 'trend.limitation': 'Reads are not scan-synchronized and may miss transitions. Online logic matching this export is unverified.',\n title: 'Studio 5000',\n workspace: 'Workspace',\n refresh: 'Refresh',\n rescan: 'Rescan',\n recent: 'Recently opened',\n projects: 'Projects',\n open: 'Open in Studio 5000',\n filter: 'Filter ACD projects',\n 'fallback.open': 'Open with Windows default',\n 'fallback.description': 'Handler discovery is unavailable or found no Studio handler. This opens the selected ACD using its system default application.',\n 'status.defaultOpened': 'Sent to the system default application. Confirm the correct project opened.',\n 'launch.description': 'Open projects in installed Studio 5000.',\n 'status.desktop': 'No desktop is available on this host.',\n 'status.discovery': 'Could not check file handlers. Update the host or check the connection.',\n 'status.handler': 'No Studio 5000 file handler found. Check the installation and ACD file association.',\n reveal: 'Reveal in Explorer',\n 'empty.none': 'No .acd files found in this workspace.',\n 'empty.scanning': 'Scanning the workspace…',\n 'empty.noSession': 'This workspace has no Session yet, so its files cannot be listed.',\n 'empty.noWorkspace': 'No workspace yet. Open a project folder first.',\n 'unavailable.listing': 'Workspace file listing is not available in this profile.',\n 'unavailable.launch': 'This host cannot open local files.',\n 'unavailable.desktop': 'No desktop application is available on this host.',\n 'status.opened': 'Open request sent. Confirm the project opened in Studio 5000.',\n 'status.revealed': 'Revealed in the file manager.',\n 'status.failed': 'Could not open the project.',\n 'status.scanFailed': 'Some directories could not be read.',\n 'status.truncated': 'The listing was truncated.',\n size: 'Size',\n truncated: '(truncated)',\n} satisfies Record<keyof typeof zh, string>\n","/** Bounded, gap-preserving plot data from the authenticated job stream. */\nexport interface TrendPoint { elapsed: number; overrun: boolean | undefined; values: { tag: string; value: number | boolean | null; quality: string }[] }\n/** Start receipt persisted in the tool result. */\nexport interface TrendReceipt { jobId: string; sessionId: string; interval: number; sourceHash: string; refresh: number; maxSamples: number }\n\n/** Parse a recorded start receipt. @param content - Logged tool content. @returns Valid receipt or undefined. */\nexport function trendReceipt(content: unknown): TrendReceipt | undefined {\n if (!Array.isArray(content)) return undefined\n for (const entry of content) {\n if (!entry || typeof entry !== 'object' || entry.type !== 'text' || typeof entry.text !== 'string') continue\n try {\n const value: unknown = JSON.parse(entry.text)\n if (value && typeof value === 'object' && 'view' in value && value.view === 'plc-trend'\n && 'job_id' in value && typeof value.job_id === 'string' && /^plc-trend-\\d+$/u.test(value.job_id)\n && 'owner_session_id' in value && typeof value.owner_session_id === 'string'\n && 'requested_interval_ms' in value && typeof value.requested_interval_ms === 'number'\n && 'source_sha256' in value && typeof value.source_sha256 === 'string'\n && 'display_refresh_ms' in value && typeof value.display_refresh_ms === 'number' && value.display_refresh_ms >= 50 && value.display_refresh_ms <= 10000\n && 'display_max_samples' in value && typeof value.display_max_samples === 'number' && value.display_max_samples >= 2 && value.display_max_samples <= 20000)\n return { jobId: value.job_id, sessionId: value.owner_session_id, interval: value.requested_interval_ms, sourceHash: value.source_sha256,\n refresh: value.display_refresh_ms, maxSamples: value.display_max_samples }\n } catch (error) { void error /* Failed tool output is rendered as a state, not plot data. */ }\n }\n return undefined\n}\n\n/** Parse one sampler line. @param line - Retained job output. @returns Plot point or undefined for other frames. */\nexport function trendPoint(line: string): TrendPoint | undefined {\n const value: unknown = JSON.parse(line)\n if (!value || typeof value !== 'object' || !('type' in value) || value.type !== 'sample') return undefined\n if (!('elapsed_ms' in value) || typeof value.elapsed_ms !== 'number' || !Number.isFinite(value.elapsed_ms)\n || !('values' in value) || !Array.isArray(value.values)) throw new Error('Invalid trend sample')\n const values = value.values.map((item: unknown) => {\n if (!item || typeof item !== 'object' || !('tag' in item) || typeof item.tag !== 'string'\n || !('quality' in item) || typeof item.quality !== 'string' || !('value' in item)\n || !(item.value === null || typeof item.value === 'boolean' || typeof item.value === 'number' && Number.isFinite(item.value)))\n throw new Error('Invalid trend value')\n return { tag: item.tag, quality: item.quality, value: item.value }\n })\n return { elapsed: value.elapsed_ms, overrun: 'overrun' in value && typeof value.overrun === 'boolean' ? value.overrun : undefined, values }\n}\n\n/** Build distinct line segments so bad samples do not imply continuity. @param points - Retained samples. @param tag - Scoped tag. @returns SVG line segments and range. */\nexport function trendSegments(points: readonly TrendPoint[], tag: string) {\n const finite = points.flatMap(point => point.values.filter(item => item.tag === tag && item.quality === 'good' && item.value !== null).map(item => Number(item.value)))\n const min = Math.min(0, ...finite); const max = Math.max(1, ...finite)\n const start = points[0]?.elapsed ?? 0; const duration = Math.max(1, (points.at(-1)?.elapsed ?? start) - start)\n const segments: string[] = []; let current: string[] = []\n for (const point of points) {\n const value = point.values.find(item => item.tag === tag)\n if (!value || value.quality !== 'good' || value.value === null) {\n if (current.length) segments.push(current.join(' ')); current = []; continue\n }\n const x = 10 + (point.elapsed - start) / duration * 580\n const y = 100 - (Number(value.value) - min) / (max - min) * 90\n if (typeof value.value === 'boolean' && current.length) current.push(`${x},${current.at(-1)?.split(',')[1]}`)\n current.push(`${x},${y}`)\n }\n if (current.length) segments.push(current.join(' '))\n return { segments, min, max }\n}\n","/** Inline live PLC trend driven by the existing session-authorized job stream. */\nimport { useEffect, useState } from 'react'\nimport type { TrendJobRemote } from './types.ts'\nimport { trendPoint, trendReceipt, trendSegments, type TrendPoint } from './trend-model.ts'\n\n/** Narrow owner/injected props supplied by the keyed tool seat. */\nexport interface TrendCardProps {\n phase: string\n block: { content?: unknown; isError?: boolean }\n trendJobs?: TrendJobRemote | undefined\n t(key: string): string\n}\n\n/** Display a live watch and stop it without cancelling the chat. @param props - Tool seat and existing job Remote. @returns Trend card. */\nexport function TrendCard({ phase, block, trendJobs, t }: TrendCardProps) {\n const receipt = phase === 'result' ? trendReceipt(block.content) : undefined\n const [points, setPoints] = useState<TrendPoint[]>([])\n const [state, setState] = useState('trend.connecting')\n const [stopping, setStopping] = useState(false)\n useEffect(() => {\n if (!receipt || !trendJobs) return\n const abort = new AbortController()\n let pending: TrendPoint[] = []; let dirty = false; let tail = ''\n const flush = () => { if (!dirty || abort.signal.aborted) return; dirty = false; setPoints([...pending]) }\n const timer = setInterval(flush, receipt.refresh)\n const run = async () => {\n try {\n for await (const frame of trendJobs.follow({ jobId: receipt.jobId, sessionId: receipt.sessionId }, abort.signal)) {\n if (abort.signal.aborted) return\n if (!frame || typeof frame !== 'object' || !('type' in frame)) throw new Error('Invalid job frame')\n if (frame.type === 'opened') setState('trend.running')\n if (frame.type === 'output' && 'chunks' in frame && Array.isArray(frame.chunks)) {\n if ('lossy' in frame && frame.lossy === true) { pending = []; tail = ''; setState('trend.gap') }\n for (const chunk of frame.chunks) {\n if (!chunk || typeof chunk !== 'object' || typeof chunk.text !== 'string') throw new Error('Invalid trend output')\n if (chunk.gapBefore === true) { pending = []; tail = ''; setState('trend.gap') }\n tail += chunk.text\n const lines = tail.split('\\n'); tail = lines.pop() ?? ''\n for (const line of lines) {\n const point = trendPoint(line)\n if (point) { pending.push(point); if (pending.length > receipt.maxSamples) pending.shift(); dirty = true }\n }\n }\n }\n if (frame.type === 'status' && 'job' in frame && frame.job && typeof frame.job === 'object' && 'status' in frame.job) {\n flush(); setState(frame.job.status === 'completed' ? 'trend.completed' : frame.job.status === 'killed' ? 'trend.stopped' : 'trend.failed')\n }\n }\n } catch (error) { if (!abort.signal.aborted) { void error; setState('trend.unavailable') } }\n finally { clearInterval(timer); flush() }\n }\n void run()\n return () => { abort.abort(); clearInterval(timer) }\n }, [receipt?.jobId, receipt?.sessionId, receipt?.refresh, receipt?.maxSamples, trendJobs])\n if (!receipt) return <div role=\"status\">{t(block.isError ? 'trend.failed' : 'trend.connecting')}</div>\n const tags = [...new Set(points.flatMap(point => point.values.map(value => value.tag)))]\n const stop = async () => {\n if (!trendJobs || stopping) return\n setStopping(true)\n try {\n const result = await trendJobs.kill({ jobId: receipt.jobId, sessionId: receipt.sessionId })\n if (!result.ok) setState('trend.stopFailed')\n } catch (error) { void error; setState('trend.stopFailed') }\n finally { setStopping(false) }\n }\n return <section aria-label={t('trend.title')} style={{ padding: '12px', border: '1px solid var(--dsw-alias-border-l3)', borderRadius: '8px' }}>\n <div style={{ display: 'flex', gap: '12px', alignItems: 'center' }}>\n <span>{t('trend.title')}</span><span role=\"status\">{t(trendJobs ? state : 'trend.unavailable')}</span>\n <button type=\"button\" style={{ color: 'var(--dsw-alias-label-primary)', background: 'transparent', border: '1px solid var(--dsw-alias-border-l3)', borderRadius: '6px', font: 'inherit', padding: '4px 10px' }} disabled={!trendJobs || stopping || ['trend.completed', 'trend.stopped', 'trend.failed'].includes(state)} onClick={() => { void stop() }}>{t('trend.stop')}</button>\n </div>\n <p>{t('trend.interval')}: {receipt.interval} ms · {t('trend.samples')}: {points.length}</p>\n {points.length > 1 && <p>{t('trend.actualInterval')}: {((points.at(-1)!.elapsed - points[0]!.elapsed) / (points.length - 1)).toFixed(1)} ms · {t('trend.overruns')}: {points.filter(point => point.overrun === true).length}</p>}\n {tags.map(tag => {\n const plot = trendSegments(points, tag)\n const latest = points.at(-1)?.values.find(value => value.tag === tag)\n return <figure key={tag} style={{ margin: '12px 0' }}><figcaption>{tag} · {latest?.quality === 'good' ? String(latest.value) : t('trend.badValue')} · {plot.min}–{plot.max}</figcaption>\n <svg viewBox=\"0 0 600 110\" role=\"img\" aria-label={tag} style={{ width: '100%', height: '110px' }}>\n {plot.segments.map((segment, index) => <polyline key={index} points={segment} fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\" />)}\n </svg>\n <small>{((points[0]?.elapsed ?? 0) / 1000).toFixed(2)}–{((points.at(-1)?.elapsed ?? 0) / 1000).toFixed(2)} s</small>\n </figure>\n })}\n <small>{t('trend.source')}: {receipt.sourceHash.slice(0, 12)} · {t('trend.limitation')}</small>\n </section>\n}\n","/** Inline PLC trend tool view; data and cancellation use the existing job Remote. */\nimport type { ClientScope } from './types.ts'\nimport { en, NS, zh } from './locales.ts'\nimport { TrendCard } from './TrendCard.tsx'\n\n/** Required client services; job availability is explained inside the card. */\nexport const inject = ['slots', 'locale', 'remote']\n\n/** Register the live trend card without a Studio launcher sidebar. @param ctx - Browser service context. */\nexport function apply(ctx: ClientScope): void {\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'l5x-live: trend copy')\n ctx.slots.inject('tool.call.toolview', () => ctx.slots.register({\n name: 'tool.call.toolview', key: 'start_plc_trend', locale: NS,\n inject: () => ({ trendJobs: ctx.remote.job }),\n }, TrendCard))\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Failures the model can repair from.
|
|
3
|
+
*
|
|
4
|
+
* Every expected failure carries the next call, because a refused tool call is
|
|
5
|
+
* a turn the reader has to spend. An unexpected failure is a plugin defect and
|
|
6
|
+
* says so instead of pretending to be a project problem.
|
|
7
|
+
*
|
|
8
|
+
* @module dsh-engineering-l5x/core/errors
|
|
9
|
+
*/
|
|
10
|
+
/** One expected failure, with everything the reader needs to recover. */
|
|
11
|
+
export declare class L5xError extends Error {
|
|
12
|
+
readonly code: string;
|
|
13
|
+
readonly fix: string;
|
|
14
|
+
readonly candidates: readonly string[];
|
|
15
|
+
/**
|
|
16
|
+
* @param code - Stable identifier for tests and logs.
|
|
17
|
+
* @param message - What failed, in the reader's terms.
|
|
18
|
+
* @param fix - The exact next call or check.
|
|
19
|
+
* @param candidates - Valid names when the request named something absent or ambiguous.
|
|
20
|
+
*/
|
|
21
|
+
constructor(code: string, message: string, fix: string, candidates?: readonly string[]);
|
|
22
|
+
}
|
|
23
|
+
/** The model-facing failure document. */
|
|
24
|
+
export interface ErrorEnvelope {
|
|
25
|
+
readonly error: {
|
|
26
|
+
readonly code: string;
|
|
27
|
+
readonly message: string;
|
|
28
|
+
readonly fix: string;
|
|
29
|
+
readonly candidates: readonly string[];
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Convert a thrown value into the failure document.
|
|
34
|
+
*
|
|
35
|
+
* @param error - Whatever the tool body threw.
|
|
36
|
+
* @returns The failure document; never throws.
|
|
37
|
+
*/
|
|
38
|
+
export declare function errorEnvelope(error: unknown): ErrorEnvelope;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Failures the model can repair from.
|
|
3
|
+
*
|
|
4
|
+
* Every expected failure carries the next call, because a refused tool call is
|
|
5
|
+
* a turn the reader has to spend. An unexpected failure is a plugin defect and
|
|
6
|
+
* says so instead of pretending to be a project problem.
|
|
7
|
+
*
|
|
8
|
+
* @module dsh-engineering-l5x/core/errors
|
|
9
|
+
*/
|
|
10
|
+
/** One expected failure, with everything the reader needs to recover. */
|
|
11
|
+
export class L5xError extends Error {
|
|
12
|
+
code;
|
|
13
|
+
fix;
|
|
14
|
+
candidates;
|
|
15
|
+
/**
|
|
16
|
+
* @param code - Stable identifier for tests and logs.
|
|
17
|
+
* @param message - What failed, in the reader's terms.
|
|
18
|
+
* @param fix - The exact next call or check.
|
|
19
|
+
* @param candidates - Valid names when the request named something absent or ambiguous.
|
|
20
|
+
*/
|
|
21
|
+
constructor(code, message, fix, candidates = []) {
|
|
22
|
+
super(message);
|
|
23
|
+
this.code = code;
|
|
24
|
+
this.fix = fix;
|
|
25
|
+
this.candidates = candidates;
|
|
26
|
+
this.name = 'L5xError';
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Convert a thrown value into the failure document.
|
|
31
|
+
*
|
|
32
|
+
* @param error - Whatever the tool body threw.
|
|
33
|
+
* @returns The failure document; never throws.
|
|
34
|
+
*/
|
|
35
|
+
export function errorEnvelope(error) {
|
|
36
|
+
if (error instanceof L5xError) {
|
|
37
|
+
return { error: { code: error.code, message: error.message, fix: error.fix, candidates: error.candidates } };
|
|
38
|
+
}
|
|
39
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
40
|
+
return {
|
|
41
|
+
error: {
|
|
42
|
+
code: 'INTERNAL',
|
|
43
|
+
message,
|
|
44
|
+
fix: 'This is a plugin defect, not a project condition. Report it with the arguments used, then reopen the project.',
|
|
45
|
+
candidates: [],
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|