dsh-engineering-ui 0.0.0-stage → 1.5.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.
Files changed (122) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +772 -3
  3. package/engineering/plugins/dsh-engineering-ui/cordis.patch.yml +5 -0
  4. package/engineering/plugins/dsh-engineering-ui/icon.svg +14 -0
  5. package/engineering/plugins/dsh-engineering-ui/lib/client.js +8259 -0
  6. package/engineering/plugins/dsh-engineering-ui/lib/defense-sign.js +1552 -0
  7. package/engineering/plugins/dsh-engineering-ui/lib/index.js +5633 -0
  8. package/engineering/plugins/dsh-engineering-ui/lib/job-wait-guard.js +236 -0
  9. package/engineering/plugins/dsh-engineering-ui/lib/mem-guard.js +331 -0
  10. package/engineering/plugins/dsh-engineering-ui/lib/webots.js +114 -0
  11. package/engineering/plugins/dsh-engineering-ui/locale/en.json +6 -0
  12. package/engineering/plugins/dsh-engineering-ui/locale/zh.json +6 -0
  13. package/engineering/plugins/dsh-engineering-ui/preset-engineering.patch.yml +601 -0
  14. package/engineering/skills/assembly-orchestration/SKILL.md +651 -0
  15. package/engineering/skills/cad-workflow/SKILL.md +132 -0
  16. package/engineering/skills/mode-selection/SKILL.md +660 -0
  17. package/engineering/skills/physics-in-loop/SKILL.md +238 -0
  18. package/engineering/skills/physics-in-loop/references/feadocs.md +142 -0
  19. package/engineering/skills/solidworks-bridge/SKILL.md +445 -0
  20. package/engineering/skills/sw-design/SKILL.md +112 -0
  21. package/engineering/skills/sw-design/references/autocad-workflow.md +81 -0
  22. package/engineering/skills/sw-design/references/gbt-drafting.md +71 -0
  23. package/engineering/skills/sw-to-cad/SKILL.md +121 -0
  24. package/engineering/skills/webots-simulation/SKILL.md +58 -0
  25. package/engineering/tools/LICENSE.Auto-SolidWorks +21 -0
  26. package/engineering/tools/_plugin_paths.py +93 -0
  27. package/engineering/tools/_root.py +330 -0
  28. package/engineering/tools/_store.py +446 -0
  29. package/engineering/tools/ac_bridge.py +459 -0
  30. package/engineering/tools/ac_rules.json +21 -0
  31. package/engineering/tools/ac_validate.py +699 -0
  32. package/engineering/tools/ask_user.py +887 -0
  33. package/engineering/tools/build_arm1.py +46 -0
  34. package/engineering/tools/build_assembly.py +94 -0
  35. package/engineering/tools/build_base.py +57 -0
  36. package/engineering/tools/build_housing.py +70 -0
  37. package/engineering/tools/build_structural.py +43 -0
  38. package/engineering/tools/build_transmission.py +69 -0
  39. package/engineering/tools/cadx.lsp +128 -0
  40. package/engineering/tools/choice_contract.py +61 -0
  41. package/engineering/tools/conn_state.py +1671 -0
  42. package/engineering/tools/defense_gate.py +3374 -0
  43. package/engineering/tools/drawing_input.py +314 -0
  44. package/engineering/tools/env_check.py +467 -0
  45. package/engineering/tools/examples/build_axis.py +142 -0
  46. package/engineering/tools/examples/build_cup.py +56 -0
  47. package/engineering/tools/examples/build_plate.py +58 -0
  48. package/engineering/tools/examples/build_sphere.py +30 -0
  49. package/engineering/tools/examples/build_transition_duct.py +381 -0
  50. package/engineering/tools/gate_integrity.py +401 -0
  51. package/engineering/tools/mode2_flow.py +3589 -0
  52. package/engineering/tools/mode_checker.py +77 -0
  53. package/engineering/tools/mode_gate.py +8013 -0
  54. package/engineering/tools/part_registry.py +4266 -0
  55. package/engineering/tools/physics/__init__.py +50 -0
  56. package/engineering/tools/physics/_compat.py +12 -0
  57. package/engineering/tools/physics/design_state.py +142 -0
  58. package/engineering/tools/physics/domain_validator.py +751 -0
  59. package/engineering/tools/physics/fatigue.py +489 -0
  60. package/engineering/tools/physics/fea_solver.py +1163 -0
  61. package/engineering/tools/physics/feapy_solver.py +314 -0
  62. package/engineering/tools/physics/gb_materials.json +1228 -0
  63. package/engineering/tools/physics/geometry_gate.py +337 -0
  64. package/engineering/tools/physics/load_case.py +951 -0
  65. package/engineering/tools/physics/material_db.py +874 -0
  66. package/engineering/tools/physics/mesh_adapter.py +321 -0
  67. package/engineering/tools/physics/refine_rules.py +272 -0
  68. package/engineering/tools/physics/rules/gb_base_rules.json +377 -0
  69. package/engineering/tools/physics/rules/gb_drafting_rules.json +57 -0
  70. package/engineering/tools/physics/rules/gb_electrical_rules.json +25 -0
  71. package/engineering/tools/physics/rules/gb_fasteners_rules.json +73 -0
  72. package/engineering/tools/physics/rules/gb_housing_rules.json +41 -0
  73. package/engineering/tools/physics/rules/gb_manufacturing_rules.json +25 -0
  74. package/engineering/tools/physics/rules/gb_material_verify_rules.json +72 -0
  75. package/engineering/tools/physics/rules/gb_materials_rules.json +377 -0
  76. package/engineering/tools/physics/rules/gb_safety_rules.json +89 -0
  77. package/engineering/tools/physics/rules/gb_springs_rules.json +41 -0
  78. package/engineering/tools/physics/rules/gb_standards_registry.json +1646 -0
  79. package/engineering/tools/physics/rules/gb_structural_rules.json +201 -0
  80. package/engineering/tools/physics/rules/gb_surface_rules.json +25 -0
  81. package/engineering/tools/physics/rules/gb_transmission_rules.json +233 -0
  82. package/engineering/tools/physics/rules/housing_rules.json +48 -0
  83. package/engineering/tools/physics/rules/manifest.json +54 -0
  84. package/engineering/tools/physics/rules/mold_rules.json +48 -0
  85. package/engineering/tools/physics/rules/structural_rules.json +56 -0
  86. package/engineering/tools/physics/rules/transmission_rules.json +48 -0
  87. package/engineering/tools/physics/simulation_report.py +829 -0
  88. package/engineering/tools/physics_bridge.py +2685 -0
  89. package/engineering/tools/profile-manifest.ps1 +72 -0
  90. package/engineering/tools/run_regression.py +109 -0
  91. package/engineering/tools/settings.py +419 -0
  92. package/engineering/tools/solidworks-modeling.md +519 -0
  93. package/engineering/tools/sw_bridge.py +8215 -0
  94. package/engineering/tools/sw_guard.py +1066 -0
  95. package/engineering/tools/sw_surface.py +87 -0
  96. package/engineering/tools/swapi.py +14766 -0
  97. package/engineering/tools/verify_sync.py +218 -0
  98. package/engineering/tools/visual_check.py +2589 -0
  99. package/engineering/tools/webots_env.py +121 -0
  100. package/engineering/tools/webots_mcp/__init__.py +3 -0
  101. package/engineering/tools/webots_mcp/__main__.py +3 -0
  102. package/engineering/tools/webots_mcp/assembly.py +391 -0
  103. package/engineering/tools/webots_mcp/bootstrap.py +55 -0
  104. package/engineering/tools/webots_mcp/controller_worker.py +124 -0
  105. package/engineering/tools/webots_mcp/evidence.py +288 -0
  106. package/engineering/tools/webots_mcp/fake_worker.py +69 -0
  107. package/engineering/tools/webots_mcp/fault_resources.py +121 -0
  108. package/engineering/tools/webots_mcp/host.py +45 -0
  109. package/engineering/tools/webots_mcp/ledger.py +73 -0
  110. package/engineering/tools/webots_mcp/owned_job.py +70 -0
  111. package/engineering/tools/webots_mcp/physical_gate.py +187 -0
  112. package/engineering/tools/webots_mcp/physics.py +152 -0
  113. package/engineering/tools/webots_mcp/pipeline.py +108 -0
  114. package/engineering/tools/webots_mcp/recipes.py +129 -0
  115. package/engineering/tools/webots_mcp/repair.py +132 -0
  116. package/engineering/tools/webots_mcp/repair_trial.py +375 -0
  117. package/engineering/tools/webots_mcp/runtime.py +311 -0
  118. package/engineering/tools/webots_mcp/server.py +217 -0
  119. package/engineering/tools/webots_mcp/sw_export.py +509 -0
  120. package/engineering/tools/webots_mcp/transport.py +156 -0
  121. package/engineering/tools/workflow_gate.py +6134 -0
  122. package/package.json +64 -4
@@ -0,0 +1,114 @@
1
+ // Host context gate; execution remains in the existing MCP server and S04 reader.
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import crypto from 'node:crypto';
5
+ import { execFile as execFileCallback } from 'node:child_process';
6
+ import { promisify } from 'node:util';
7
+ const execFile = promisify(execFileCallback);
8
+ const ID = /^[A-Za-z0-9][A-Za-z0-9_-]{0,95}(?![\s\S])/;
9
+ const PREFIX = '/dsh-engineering-ui/';
10
+ function objectFile(file) {
11
+ const stat = fs.lstatSync(file);
12
+ if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 1048576) throw Error('Invalid context file');
13
+ const value = JSON.parse(fs.readFileSync(file, 'utf8'));
14
+ if (!value || Array.isArray(value) || typeof value !== 'object') throw Error('Invalid context object');
15
+ return value;
16
+ }
17
+ export function resolveWebotsContext(ctx, agent, options) {
18
+ const env = options.env || process.env;
19
+ const declared = env.DSH_STATE_DIR;
20
+ if (!declared || !path.isAbsolute(declared) || !fs.statSync(declared).isDirectory()) throw Error('Explicit existing state directory required');
21
+ const state = fs.realpathSync(declared);
22
+ if (fs.realpathSync(options.findToolsDir()) !== state) throw Error('State directory mismatch');
23
+ if (!agent?.id || ctx.agents.get(agent.id) !== agent || options.presetOf(agent) !== 'engineering') throw Error('Live engineering agent required');
24
+ const workflow = objectFile(path.join(state, 'workflow_state.json'));
25
+ const mode = objectFile(path.join(state, 'mode_state.json'));
26
+ if (typeof workflow.task_id !== 'string' || !ID.test(workflow.task_id)) throw Error('Current task unavailable');
27
+ const rooms = Object.entries(mode.rooms || {}).filter(([room, record]) =>
28
+ room && record?.active === true && record.task_id === workflow.task_id && mode.subagents?.[room]?.id === agent.id);
29
+ if (rooms.length !== 1) throw Error('Unique active same-task room binding required');
30
+ const room = rooms[0][0];
31
+ return { task_id: workflow.task_id, room_id: 'room-' + crypto.createHash('sha256').update(room, 'utf8').digest('hex'),
32
+ room_label: room, state_dir: state };
33
+ }
34
+ export function webotsGuard(ctx, options) {
35
+ return exec => {
36
+ if (!exec.name?.startsWith('mcp__webots__')) return;
37
+ try {
38
+ const own = resolveWebotsContext(ctx, exec.agent, options);
39
+ if (exec.arguments?.task_id !== own.task_id || exec.arguments?.room_id !== own.room_id) throw Error('Request ownership mismatch');
40
+ } catch (error) { return 'CONTEXT_UNAVAILABLE: ' + error.message; }
41
+ };
42
+ }
43
+ // ⚠️ ctx.tools 的优雅降级(本工程既有约束):
44
+ // · 宿主组合里若没有 @deepseek-ai/dsh-tool-* / tools 服务,ctx.tools 为 undefined;
45
+ // · 插件测试桩、旧宿主也可能没注册 guard。
46
+ // 此时【绝不能让整块装配抛异常】—— 那会让 import 求值失败、插件整体不装配,
47
+ // 表现为"设置页打不开 / 连接区整块消失",比缺一个守卫严重得多。
48
+ // 降级语义是 fail-closed 的反面吗?不是:没有 tools 服务 = 根本没有 mcp__webots__*
49
+ // 工具可执行(MCP 行默认禁用),守卫无事可守;只读路由仍走原有 guardRoute。
50
+ export function installWebots(ctx, options) {
51
+ const disposers = [];
52
+ try {
53
+ const guard = ctx && ctx.tools && typeof ctx.tools.guard === 'function'
54
+ ? ctx.tools.guard.bind(ctx.tools)
55
+ : null;
56
+ if (guard) disposers.push(guard(webotsGuard(ctx, options)));
57
+ } catch { /* 守卫不可用:不影响只读路由与插件装配 */ }
58
+ for (const kind of ['context', 'report', 'evidence', 'attribute']) {
59
+ disposers.push(ctx.webServer.register(options.guardRoute({ kind: 'exact', path: PREFIX + 'webots-' + kind,
60
+ handler: async (req, res) => {
61
+ const reply = (status, body) => options.send(res, status, body);
62
+ if (req.method !== 'GET') return reply(405, {ok:false,error_code:'METHOD_NOT_ALLOWED'});
63
+ const query = options.queryOf(req);
64
+ const allowed = ['sessionId', ...(kind === 'context' ? [] : ['run_id']), ...(kind === 'report' ? ['report_type'] : kind === 'evidence' ? ['name','offset','limit','expected_sha256'] : kind === 'attribute' ? ['issue_id'] : [])];
65
+ if ([...query.keys()].some(key => !allowed.includes(key)) || [...query.keys()].some(key => query.getAll(key).length !== 1)) return reply(400,{ok:false,error_code:'INVALID_ARGUMENT'});
66
+ let own;
67
+ try { own = resolveWebotsContext(ctx, ctx.agents.get(query.get('sessionId')), options); }
68
+ catch (error) { return reply(403, {ok:false,error_code:'CONTEXT_UNAVAILABLE',hint:error.message}); }
69
+ const base = { ...own, core_ready:false, execution_state:'not_started', job_state:'NEW', provenance:'unavailable',
70
+ snapshot_at:new Date().toISOString(), stale:true, sim_time:null, completed_steps:null,
71
+ unsupported:['unconfigured_runtime','native_export','native_repair'],
72
+ capabilities:['stored_reports','bounded_evidence','attribution','pinned_runtime','durable_operations'],
73
+ blocked_native:['native_mesh_frame','complete_global_mate_state','native_identity_calibration','converter_physical_precision','calibrated_collision_feedback','authorized_native_repair'] };
74
+ if (kind === 'context') {
75
+ try {
76
+ const s = objectFile(path.join(own.state_dir,'webots_state.json'));
77
+ const stamp = Date.parse(s.snapshot_at), until = Date.parse(s.valid_until), now = Date.now();
78
+ if (s.schema_version !== 1 || s.task_id !== own.task_id || s.room_id !== own.room_id || !ID.test(s.run_id || '')
79
+ || !Number.isSafeInteger(s.session_generation) || s.session_generation < 1) throw Error('Unbound projection');
80
+ const fresh = Number.isFinite(stamp) && Number.isFinite(until) && stamp <= now && until > now && until-stamp <= 3000;
81
+ for (const key of ['run_id','session_generation','job_state','snapshot_at','sim_time','completed_steps','provenance','diagnostic']) if (key in s) base[key]=s[key];
82
+ base.stale = !fresh || s.stale !== false;
83
+ base.core_ready = !base.stale && s.core_ready === true && s.provenance === 'real-webots';
84
+ if (fresh) base.unsupported = ['native_export','native_repair'];
85
+ } catch { /* No verified same-room cached runtime; conservative base remains. */ }
86
+ return reply(200,{...base,ok:true,error_code:base.core_ready ? null : 'RUNTIME_NOT_READY'});
87
+ }
88
+ const number = (key, min, max) => !query.has(key) || (/^[0-9]+(?![\s\S])/.test(query.get(key)) && Number.isSafeInteger(Number(query.get(key))) && Number(query.get(key)) >= min && Number(query.get(key)) <= max);
89
+ if (!ID.test(query.get('run_id') || '') || (kind === 'report' && !['model_manifest','conversion_report','experiment_report'].includes(query.get('report_type')))
90
+ || (kind === 'attribute' && !ID.test(query.get('issue_id') || ''))
91
+ || (query.has('name') && !ID.test(query.get('name'))) || !number('offset',0,Number.MAX_SAFE_INTEGER) || !number('limit',4,16384)
92
+ || (query.has('expected_sha256') && !/^[a-f0-9]{64}(?![\s\S])/.test(query.get('expected_sha256')))) return reply(400,{...base,ok:false,error_code:'INVALID_ARGUMENT'});
93
+ const env = options.env || process.env;
94
+ if (!env.DSH_WEBOTS_PYTHON || !path.isAbsolute(env.DSH_WEBOTS_PYTHON) || !env.DSH_ENGINEERING_ROOT || !path.isAbsolute(env.DSH_ENGINEERING_ROOT)) return reply(503,{...base,ok:false,error_code:'READER_UNAVAILABLE'});
95
+ const args = ['-B','-m','webots_mcp.host',kind,'--task-id',own.task_id,'--room-id',own.room_id];
96
+ for (const key of allowed.filter(key => key !== 'sessionId')) if (query.has(key)) args.push('--' + key.replaceAll('_','-'),query.get(key));
97
+ let result;
98
+ try { result = await execFile(env.DSH_WEBOTS_PYTHON,args,{cwd:path.join(env.DSH_ENGINEERING_ROOT,'tools'),
99
+ env:{...env,DSH_STATE_DIR:own.state_dir,PYTHONIOENCODING:'utf-8',PYTHONUTF8:'1'},encoding:'utf8',windowsHide:true,timeout:15000,maxBuffer:2*1048576}); }
100
+ catch { return reply(503,{...base,ok:false,error_code:'READER_UNAVAILABLE'}); }
101
+ try {
102
+ const current = resolveWebotsContext(ctx, ctx.agents.get(query.get('sessionId')), options);
103
+ if (['task_id','room_id','state_dir'].some(key => current[key] !== own[key])) throw Error('Binding changed during read');
104
+ } catch (error) { return reply(403,{ok:false,error_code:'CONTEXT_UNAVAILABLE',hint:error.message}); }
105
+ try {
106
+ const data = JSON.parse(result.stdout);
107
+ const status = data.ok ? 200 : data.error_code === 'OWNERSHIP_MISMATCH' ? 403 : data.error_code === 'EVIDENCE_NOT_FOUND' ? 404 : 400;
108
+ return reply(status,{...base,...data});
109
+ } catch { return reply(503,{...base,ok:false,error_code:'READER_UNAVAILABLE'}); }
110
+ }
111
+ })));
112
+ }
113
+ return () => { for (const dispose of disposers.reverse()) dispose(); };
114
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "Engineering Mode",
4
+ "description": "Sub-agent three-panel workspace for DSH Engineering Mode: main conversation + channel list + live workspace. Active only under the engineering preset; can be disabled in Settings."
5
+ }
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "工程模式",
4
+ "description": "DSH 工程模式 UI:子代理三栏工作区(主对话 + 频道列表 + 实时工作区),仅在 engineering 预设下生效,可在设置中关闭。"
5
+ }
6
+ }