dsh-cad 0.2.0 → 0.7.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.
@@ -0,0 +1,24 @@
1
+ /** The solver's solve report (outcome + solved entities + diagnostics). */
2
+ export interface AnsatzSolveReport {
3
+ outcome: string;
4
+ entities: Array<{
5
+ id: number;
6
+ geometry?: Record<string, unknown>;
7
+ }>;
8
+ diagnostics?: Record<string, unknown>;
9
+ }
10
+ /**
11
+ * Resolve the wasm solver package directory, or null when none is present.
12
+ * The npm dependency wins, so `npm install ansatz-wasm@latest` takes effect.
13
+ */
14
+ export declare function resolveAnsatzWasmDir(explicit?: string): string | null;
15
+ /** Whether the solver is available (diagnostics/tests). */
16
+ export declare function ansatzAvailable(): boolean;
17
+ /** Solve one model with the wasm solver. */
18
+ export declare function solveModel(model: unknown, options?: {
19
+ wasmDir?: string;
20
+ }): Promise<AnsatzSolveReport>;
21
+ /** Solver version string, or null when the solver is unavailable. */
22
+ export declare function ansatzVersion(options?: {
23
+ wasmDir?: string;
24
+ }): string | null;
@@ -14,6 +14,8 @@ export declare class BinarySceneStore {
14
14
  publish(viewId: string, meshes: BinMeshData[]): Promise<number>;
15
15
  /** Fetch for serving: memory first, disk mirror as the restart fallback. */
16
16
  get(viewId: string): Promise<Entry | null>;
17
+ /** Existence check without loading the buffer into memory. */
18
+ has(viewId: string): Promise<boolean>;
17
19
  /** Debounced disk mirror: one write after modeling quiesces, not per step. */
18
20
  private scheduleDiskMirror;
19
21
  }
@@ -36,6 +36,12 @@ export interface OpResult {
36
36
  views?: DrawingView[];
37
37
  instanceId?: string;
38
38
  instances?: AssemblyInstance[];
39
+ /** Pattern op: the newly created copy bodies with their meshes. */
40
+ created?: Array<{
41
+ bodyId: string;
42
+ name?: string;
43
+ mesh?: WorkerMesh;
44
+ }>;
39
45
  }
40
46
  export interface DrawingViewSpec {
41
47
  name: string;
@@ -51,10 +57,45 @@ export type ModelOp = {
51
57
  } | {
52
58
  kind: 'extrude_profile';
53
59
  bodyId: string;
54
- points: number[];
60
+ points?: number[];
61
+ profile?: unknown;
55
62
  height?: number;
56
63
  base?: number;
57
64
  name?: string;
65
+ } | {
66
+ kind: 'loft';
67
+ bodyId: string;
68
+ sections: number[][];
69
+ solid?: boolean;
70
+ ruled?: boolean;
71
+ name?: string;
72
+ } | {
73
+ kind: 'revolve';
74
+ bodyId: string;
75
+ profile: unknown;
76
+ angle?: number;
77
+ axis?: [number, number, number];
78
+ at?: [number, number, number];
79
+ name?: string;
80
+ } | {
81
+ kind: 'chamfer';
82
+ target: string;
83
+ distance: number;
84
+ } | {
85
+ kind: 'pattern';
86
+ target: string;
87
+ mode: 'linear' | 'circular';
88
+ count: number;
89
+ delta?: [number, number, number];
90
+ axis?: [number, number, number];
91
+ at?: [number, number, number];
92
+ angle?: number;
93
+ } | {
94
+ kind: 'sweep';
95
+ bodyId: string;
96
+ profile: unknown;
97
+ path: number[];
98
+ name?: string;
58
99
  } | {
59
100
  kind: 'boolean';
60
101
  op: 'fuse' | 'cut' | 'common';
@@ -106,6 +147,14 @@ export type ModelOp = {
106
147
  } | {
107
148
  kind: 'assembly_remove';
108
149
  instanceId: string;
150
+ } | {
151
+ kind: 'assembly_list';
152
+ } | {
153
+ kind: 'constraints';
154
+ model: {
155
+ entities: unknown[];
156
+ constraints: unknown[];
157
+ };
109
158
  } | {
110
159
  kind: 'export_assembly';
111
160
  format: 'step' | 'stl';
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Constraint-model helpers: map dsh-cad assembly instances (translate + XYZ
3
+ * Euler degrees, matching cad_transform / assembly.ts) to and from the
4
+ * Ansatz solver's `rigid3` pose (translation + rotation as an exponential-map
5
+ * vector — direction = axis, magnitude = radians), plus the persisted
6
+ * constraint-model types shared by the cad_constraint / cad_solve /
7
+ * cad_motion tools.
8
+ */
9
+ import type { AssemblyInstance } from './client.js';
10
+ export interface SolverVec3 {
11
+ x: number;
12
+ y: number;
13
+ z: number;
14
+ }
15
+ /** Ansatz rigid3 pose: translation + exponential-map rotation vector. */
16
+ export interface Rigid3Pose {
17
+ translation: SolverVec3;
18
+ rotation: {
19
+ vector: [number, number, number];
20
+ };
21
+ }
22
+ /** One entity of the persisted constraint model. */
23
+ export interface ConstraintEntity {
24
+ id: number;
25
+ /** Assembly instance this entity tracks (geometry stays in sync on solve). */
26
+ instance?: string;
27
+ /** Literal geometry (point2 …) or a rigid3 snapshot; omitted for instance-bound entities until solve time. */
28
+ geometry?: Record<string, unknown>;
29
+ }
30
+ /** One constraint, pass-through to the solver's schema (type + refs + params). */
31
+ export interface ConstraintEntry {
32
+ id: number;
33
+ kind: Record<string, unknown>;
34
+ label?: string;
35
+ }
36
+ /** The persisted constraint model (op log entry; the worker stores it as a no-op). */
37
+ export interface ConstraintModel {
38
+ entities: ConstraintEntity[];
39
+ constraints: ConstraintEntry[];
40
+ }
41
+ /** Instance placement → solver rigid3 pose. */
42
+ export declare function instanceToRigid3(instance: AssemblyInstance): Rigid3Pose;
43
+ /** Solver rigid3 pose → instance placement (translate tuple + Euler degrees). */
44
+ export declare function rigid3ToInstance(pose: Rigid3Pose): {
45
+ translate: [number, number, number];
46
+ rotate: [number, number, number];
47
+ };
48
+ /** Build the solver-input model: instance-bound entities get live rigid3 poses. */
49
+ export declare function buildSolverModel(model: ConstraintModel, instances: AssemblyInstance[]): {
50
+ entities: Array<{
51
+ id: number;
52
+ geometry: Record<string, unknown>;
53
+ }>;
54
+ constraints: ConstraintEntry[];
55
+ };
56
+ /** Extract solved rigid3 poses keyed by entity id (absent for 2D geometry). */
57
+ export declare function solvedRigid3s(reportEntities: Array<{
58
+ id: number;
59
+ geometry?: {
60
+ type?: string;
61
+ pose?: Rigid3Pose;
62
+ };
63
+ }>): Map<number, Rigid3Pose>;
@@ -11,7 +11,15 @@ export interface ModelDoc {
11
11
  export declare class ModelDocument {
12
12
  readonly root: string;
13
13
  doc: ModelDoc;
14
- constructor(root: string);
14
+ /**
15
+ * @param docId Registry-assigned document id. With an id the document lives
16
+ * at `<root>/.dsh-cad/docs/<docId>.json`; without one it keeps the legacy
17
+ * single-document path `<root>/.dsh-cad/model.json` (tests, migration).
18
+ */
19
+ constructor(root: string, docId?: string);
20
+ /** True when this instance owns the legacy single-document path. */
21
+ private readonly legacy;
22
+ private get base();
15
23
  private get directory();
16
24
  private get file();
17
25
  /** Load the persisted document if one exists. */
@@ -21,6 +29,8 @@ export declare class ModelDocument {
21
29
  bodyId: string;
22
30
  name: string;
23
31
  } | null): Promise<void>;
32
+ /** Persist the current state without appending an op (registry creation). */
33
+ save(): Promise<void>;
24
34
  /** Clear the document (cad_new / tests). */
25
35
  clear(): Promise<void>;
26
36
  }
@@ -0,0 +1,61 @@
1
+ import { ModelDocument } from './document.js';
2
+ export interface DocMeta {
3
+ id: string;
4
+ name: string;
5
+ /** ISO timestamps; updatedAt drives the file-list ordering. */
6
+ createdAt: string;
7
+ updatedAt: string;
8
+ opCount: number;
9
+ bodyCount: number;
10
+ }
11
+ export declare class DocumentRegistry {
12
+ readonly root: string;
13
+ private state;
14
+ private loaded;
15
+ constructor(root: string);
16
+ private get base();
17
+ private get docsDir();
18
+ private get file();
19
+ private docFile;
20
+ /** Load the manifest once; migrates the legacy single document on first run. */
21
+ private ensureLoaded;
22
+ /**
23
+ * Adopt the pre-registry single document (`.dsh-cad/model.json`) as a named
24
+ * document so existing work stays openable from the file list.
25
+ */
26
+ private migrateLegacy;
27
+ private persist;
28
+ /** All document metas, most recently updated first. */
29
+ list(): Promise<DocMeta[]>;
30
+ /** Create a named document (empty op log, persisted immediately). */
31
+ create(name?: string): Promise<ModelDocument>;
32
+ private nextUntitledName;
33
+ /** Open a document by id (null when the manifest has no such entry). */
34
+ open(id: string): Promise<ModelDocument | null>;
35
+ /** Resolve by document id or (case-insensitive) exact name. */
36
+ resolve(ref: string): Promise<DocMeta | null>;
37
+ /** Bind a session to its active document. */
38
+ bind(sessionId: string, docId: string): Promise<void>;
39
+ /** The session's active document id (null: unbound — create on first use). */
40
+ bindingOf(sessionId: string): Promise<string | null>;
41
+ /**
42
+ * One-shot upgrade continuity: hand the migrated legacy document to the
43
+ * first unbound session (so continuing an old conversation keeps its
44
+ * bodies) and clear the marker. Returns null once claimed or absent.
45
+ */
46
+ claimLegacyFor(sessionId: string): Promise<string | null>;
47
+ /** Update a document's meta after recorded ops (write-through). */
48
+ touch(docId: string, patch: {
49
+ opCount?: number;
50
+ bodyCount?: number;
51
+ name?: string;
52
+ }): Promise<void>;
53
+ /** Rename a document. */
54
+ rename(id: string, name: string): Promise<DocMeta | null>;
55
+ /**
56
+ * Delete a document: remove its op log, drop every session binding to it.
57
+ * Scene caches (bin mirrors) are inert without the manifest entry and are
58
+ * left for the store's own lifecycle.
59
+ */
60
+ remove(id: string): Promise<boolean>;
61
+ }
@@ -1,9 +1,12 @@
1
1
  import type { IncomingMessage, ServerResponse } from 'node:http';
2
2
  import type { SceneStore } from './store.js';
3
3
  import type { BinarySceneStore } from './modeling/bin-store.js';
4
+ import type { DocumentRegistry } from './modeling/registry.js';
4
5
  export declare const SCENE_ROUTE_PATH = "/dsh-cad/scene";
5
6
  export declare const BIN_ROUTE_PATH = "/dsh-cad/bin";
6
7
  export declare const DEMO_SCENE_ROUTE_PATH = "/dsh-cad/demo-scene";
8
+ export declare const DOCS_ROUTE_PATH = "/dsh-cad/docs";
9
+ export declare const DOCS_DELETE_ROUTE_PATH = "/dsh-cad/docs/delete";
7
10
  /** The built-in demo examples (packaged as lib/demo-<part>.brep). */
8
11
  export declare const DEMO_PARTS: readonly ["bracket", "flange", "shaft"];
9
12
  export type DemoPart = (typeof DEMO_PARTS)[number];
@@ -24,4 +27,12 @@ export declare function registerBinRoute(server: {
24
27
  export declare function registerDemoRoute(server: {
25
28
  register: (route: SceneRoute) => () => void;
26
29
  }): () => void;
30
+ /** Register the docs file-space route: GET /dsh-cad/docs (list documents). */
31
+ export declare function registerDocsRoute(server: {
32
+ register: (route: SceneRoute) => () => void;
33
+ }, registry: DocumentRegistry, binStore: BinarySceneStore): () => void;
34
+ /** Register the docs delete route: POST /dsh-cad/docs/delete?id=<docId>. */
35
+ export declare function registerDocsDeleteRoute(server: {
36
+ register: (route: SceneRoute) => () => void;
37
+ }, registry: DocumentRegistry): () => void;
27
38
  export type { SceneRoute };
@@ -0,0 +1,25 @@
1
+ import type { ToolDefinition } from '@deepseek-ai/dsh-tools';
2
+ import type { ModelOp, OpResult } from '../modeling/client.js';
3
+ import type { ConstraintModel } from '../modeling/constraints.js';
4
+ import type { ModelDocument } from '../modeling/document.js';
5
+ import type { BinMeshData } from '../modeling/bin-format.js';
6
+ import type { ModelToolDeps } from './cad-model.js';
7
+ /**
8
+ * State shared with createModelTools. The active document is read through
9
+ * `getDocument()` (multi-document sessions swap it per call), and
10
+ * `resolveDoc(exec)` binds the calling session's document + replays it —
11
+ * the multi-document successor of the old restoreOnce.
12
+ */
13
+ export interface ConstraintToolCtx {
14
+ deps: ModelToolDeps;
15
+ getDocument(): ModelDocument;
16
+ meshCache: Map<string, BinMeshData>;
17
+ constraintState: {
18
+ model: ConstraintModel | null;
19
+ lastSuggestions: string[];
20
+ };
21
+ resolveDoc(exec: unknown): Promise<void>;
22
+ syncAssembly(op: ModelOp, result: OpResult, filePath?: string): Promise<Record<string, unknown>>;
23
+ assemblyMetaOf(value: Record<string, unknown>): Record<string, unknown>;
24
+ }
25
+ export declare function createConstraintTools(ctx: ConstraintToolCtx): ToolDefinition[];
@@ -1,4 +1,5 @@
1
1
  import type { ToolDefinition } from '@deepseek-ai/dsh-tools';
2
+ import { DocumentRegistry } from '../modeling/registry.js';
2
3
  import type { BinarySceneStore } from '../modeling/bin-store.js';
3
4
  import type { SceneStore } from '../store.js';
4
5
  export interface ModelToolDeps {
@@ -7,7 +8,9 @@ export interface ModelToolDeps {
7
8
  sceneStore: SceneStore;
8
9
  workspaceRoot: string;
9
10
  ensureSceneRoute: () => string | null;
11
+ /** The workspace document registry (file space + session bindings). */
12
+ registry: DocumentRegistry;
10
13
  }
11
- /** Build the whole tool family over one document + worker + scene store. */
14
+ /** Build the whole tool family over the document registry + worker + stores. */
12
15
  export declare function createModelTools(deps: ModelToolDeps): ToolDefinition[];
13
- export declare const MODEL_TOOL_NAMES: readonly ["cad_create_prim", "cad_extrude_profile", "cad_boolean", "cad_fillet", "cad_transform", "cad_export", "cad_delete", "cad_volume", "cad_drawing", "cad_assembly_insert", "cad_assembly_move", "cad_assembly_remove"];
16
+ export declare const MODEL_TOOL_NAMES: readonly ["cad_create_prim", "cad_extrude_profile", "cad_revolve", "cad_chamfer", "cad_pattern", "cad_loft", "cad_sweep", "cad_boolean", "cad_fillet", "cad_transform", "cad_export", "cad_delete", "cad_volume", "cad_drawing", "cad_assembly_insert", "cad_assembly_move", "cad_assembly_remove", "cad_docs", "cad_doc_new", "cad_doc_open", "cad_doc_rename", "cad_doc_delete", "cad_constraint", "cad_solve", "cad_motion"];
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-cad",
3
3
  "description": "CAD visualization plugin for DeepSeek Harness: cad_view / cad_info tools with an embedded 3D (STL/OBJ/STEP/IGES) and 2D (DXF/SVG) viewer card in the Web UI",
4
- "version": "0.2.0",
4
+ "version": "0.7.0",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "author": "LAU-MARS",
@@ -58,18 +58,19 @@
58
58
  },
59
59
  "files": [
60
60
  "lib",
61
- "cordis.patch.yml",
62
61
  "README.md"
63
62
  ],
64
63
  "scripts": {
65
- "build": "tsc -p tsconfig.json && node esbuild.client.mjs && node -e \"const fs=require('fs'); fs.copyFileSync('src/convert/step-worker.mjs','lib/convert/step-worker.mjs'); fs.mkdirSync('lib/modeling',{recursive:true}); fs.copyFileSync('src/modeling/occt-adapter.cjs','lib/modeling/occt-adapter.cjs'); fs.copyFileSync('src/modeling/modeling-worker.cjs','lib/modeling/modeling-worker.cjs'); fs.copyFileSync('src/modeling/hlr.cjs','lib/modeling/hlr.cjs'); for (const part of ['bracket','flange','shaft']) fs.copyFileSync('assets/demo-'+part+'.brep','lib/demo-'+part+'.brep')\"",
64
+ "build": "tsc -p tsconfig.json && node esbuild.client.mjs && node -e \"const fs=require('fs'); fs.copyFileSync('src/convert/step-worker.mjs','lib/convert/step-worker.mjs'); fs.mkdirSync('lib/modeling',{recursive:true}); fs.copyFileSync('src/modeling/occt-adapter.cjs','lib/modeling/occt-adapter.cjs'); fs.copyFileSync('src/modeling/modeling-worker.cjs','lib/modeling/modeling-worker.cjs'); fs.copyFileSync('src/modeling/occt-bridge.cjs','lib/modeling/occt-bridge.cjs'); for (const part of ['bracket','flange','shaft']) fs.copyFileSync('assets/demo-'+part+'.brep','lib/demo-'+part+'.brep')\"",
66
65
  "watch": "tsc -p tsconfig.json --watch & node esbuild.client.mjs --watch",
67
66
  "test": "vitest run --no-file-parallelism",
68
67
  "test:watch": "vitest"
69
68
  },
70
69
  "dependencies": {
70
+ "ansatz-wasm": "^0.2.0",
71
71
  "dxf-parser": "^1.1.2",
72
72
  "occt-import-js": "^0.0.23",
73
+ "occt.ts": "^0.1.0",
73
74
  "opencascade.js": "^1.1.1",
74
75
  "three": "^0.185.1"
75
76
  },
package/cordis.patch.yml DELETED
@@ -1,3 +0,0 @@
1
- - insert:
2
- - id: dsh-cad
3
- name: 'dsh-cad'